summaryrefslogtreecommitdiff
path: root/cli/js/compiler_util.ts
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2020-03-11 10:53:06 +0100
committerGitHub <noreply@github.com>2020-03-11 10:53:06 +0100
commit99a0c6df79b903e4fe72ce066787039bdede3868 (patch)
tree1c0abdb964a2e052b593dc8fa3e515f76dbc0642 /cli/js/compiler_util.ts
parent94f4c6807a34a564f567b984e71e36e9cb9c5005 (diff)
reorg: cli/js/compiler/, move more API to cli/js/web/ (#4310)
- moves compiler implementation to "cli/js/compiler/" directory - moves more APIs to "cli/js/web": * "console.ts" * "console_table.ts" * "performance.ts" * "timers.ts" * "workers.ts" - removes some dead code from "cli/js/"
Diffstat (limited to 'cli/js/compiler_util.ts')
-rw-r--r--cli/js/compiler_util.ts448
1 files changed, 0 insertions, 448 deletions
diff --git a/cli/js/compiler_util.ts b/cli/js/compiler_util.ts
deleted file mode 100644
index 3cc661d6c..000000000
--- a/cli/js/compiler_util.ts
+++ /dev/null
@@ -1,448 +0,0 @@
-// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-
-import { bold, cyan, yellow } from "./colors.ts";
-import { CompilerOptions } from "./compiler_api.ts";
-import { buildBundle } from "./compiler_bundler.ts";
-import { ConfigureResponse, Host } from "./compiler_host.ts";
-import { SourceFile } from "./compiler_sourcefile.ts";
-import { atob, TextEncoder } from "./web/text_encoding.ts";
-import * as compilerOps from "./ops/compiler.ts";
-import * as util from "./util.ts";
-import { assert } from "./util.ts";
-import { writeFileSync } from "./write_file.ts";
-
-/** Type for the write fall callback that allows delegation from the compiler
- * host on writing files. */
-export type WriteFileCallback = (
- fileName: string,
- data: string,
- sourceFiles?: readonly ts.SourceFile[]
-) => void;
-
-/** An object which is passed to `createWriteFile` to be used to read and set
- * state related to the emit of a program. */
-export interface WriteFileState {
- type: CompilerRequestType;
- bundle?: boolean;
- host?: Host;
- outFile?: string;
- rootNames: string[];
- emitMap?: Record<string, string>;
- emitBundle?: string;
- sources?: Record<string, string>;
-}
-
-// Warning! The values in this enum are duplicated in `cli/msg.rs`
-// Update carefully!
-export enum CompilerRequestType {
- Compile = 0,
- RuntimeCompile = 1,
- RuntimeTranspile = 2
-}
-
-export const OUT_DIR = "$deno$";
-
-/** Cache the contents of a file on the trusted side. */
-function cache(
- moduleId: string,
- emittedFileName: string,
- contents: string,
- checkJs = false
-): void {
- util.log("compiler::cache", { moduleId, emittedFileName, checkJs });
- const sf = SourceFile.get(moduleId);
-
- if (sf) {
- // NOTE: If it's a `.json` file we don't want to write it to disk.
- // JSON files are loaded and used by TS compiler to check types, but we don't want
- // to emit them to disk because output file is the same as input file.
- if (sf.extension === ts.Extension.Json) {
- return;
- }
-
- // NOTE: JavaScript files are only cached to disk if `checkJs`
- // option in on
- if (sf.extension === ts.Extension.Js && !checkJs) {
- return;
- }
- }
-
- if (emittedFileName.endsWith(".map")) {
- // Source Map
- compilerOps.cache(".map", moduleId, contents);
- } else if (
- emittedFileName.endsWith(".js") ||
- emittedFileName.endsWith(".json")
- ) {
- // Compiled JavaScript
- compilerOps.cache(".js", moduleId, contents);
- } else {
- assert(false, `Trying to cache unhandled file type "${emittedFileName}"`);
- }
-}
-
-/** Retrieve an asset from Rust. */
-export function getAsset(name: string): string {
- return compilerOps.getAsset(name);
-}
-
-/** Generates a `writeFile` function which can be passed to the compiler `Host`
- * to use when emitting files. */
-export function createWriteFile(state: WriteFileState): WriteFileCallback {
- const encoder = new TextEncoder();
- if (state.type === CompilerRequestType.Compile) {
- return function writeFile(
- fileName: string,
- data: string,
- sourceFiles?: readonly ts.SourceFile[]
- ): void {
- assert(
- sourceFiles != null,
- `Unexpected emit of "${fileName}" which isn't part of a program.`
- );
- assert(state.host);
- if (!state.bundle) {
- assert(sourceFiles.length === 1);
- cache(
- sourceFiles[0].fileName,
- fileName,
- data,
- state.host.getCompilationSettings().checkJs
- );
- } else {
- // if the fileName is set to an internal value, just noop, this is
- // used in the Rust unit tests.
- if (state.outFile && state.outFile.startsWith(OUT_DIR)) {
- return;
- }
- // we only support single root names for bundles
- assert(
- state.rootNames.length === 1,
- `Only one root name supported. Got "${JSON.stringify(
- state.rootNames
- )}"`
- );
- // this enriches the string with the loader and re-exports the
- // exports of the root module
- const content = buildBundle(state.rootNames[0], data, sourceFiles);
- if (state.outFile) {
- const encodedData = encoder.encode(content);
- console.warn(`Emitting bundle to "${state.outFile}"`);
- writeFileSync(state.outFile, encodedData);
- console.warn(`${humanFileSize(encodedData.length)} emitted.`);
- } else {
- console.log(content);
- }
- }
- };
- }
-
- return function writeFile(
- fileName: string,
- data: string,
- sourceFiles?: readonly ts.SourceFile[]
- ): void {
- assert(sourceFiles != null);
- assert(state.host);
- assert(state.emitMap);
- if (!state.bundle) {
- assert(sourceFiles.length === 1);
- state.emitMap[fileName] = data;
- // we only want to cache the compiler output if we are resolving
- // modules externally
- if (!state.sources) {
- cache(
- sourceFiles[0].fileName,
- fileName,
- data,
- state.host.getCompilationSettings().checkJs
- );
- }
- } else {
- // we only support single root names for bundles
- assert(state.rootNames.length === 1);
- state.emitBundle = buildBundle(state.rootNames[0], data, sourceFiles);
- }
- };
-}
-
-export interface ConvertCompilerOptionsResult {
- files?: string[];
- options: ts.CompilerOptions;
-}
-
-/** Take a runtime set of compiler options as stringified JSON and convert it
- * to a set of TypeScript compiler options. */
-export function convertCompilerOptions(
- str: string
-): ConvertCompilerOptionsResult {
- const options: CompilerOptions = JSON.parse(str);
- const out: Record<string, unknown> = {};
- const keys = Object.keys(options) as Array<keyof CompilerOptions>;
- const files: string[] = [];
- for (const key of keys) {
- switch (key) {
- case "jsx":
- const value = options[key];
- if (value === "preserve") {
- out[key] = ts.JsxEmit.Preserve;
- } else if (value === "react") {
- out[key] = ts.JsxEmit.React;
- } else {
- out[key] = ts.JsxEmit.ReactNative;
- }
- break;
- case "module":
- switch (options[key]) {
- case "amd":
- out[key] = ts.ModuleKind.AMD;
- break;
- case "commonjs":
- out[key] = ts.ModuleKind.CommonJS;
- break;
- case "es2015":
- case "es6":
- out[key] = ts.ModuleKind.ES2015;
- break;
- case "esnext":
- out[key] = ts.ModuleKind.ESNext;
- break;
- case "none":
- out[key] = ts.ModuleKind.None;
- break;
- case "system":
- out[key] = ts.ModuleKind.System;
- break;
- case "umd":
- out[key] = ts.ModuleKind.UMD;
- break;
- default:
- throw new TypeError("Unexpected module type");
- }
- break;
- case "target":
- switch (options[key]) {
- case "es3":
- out[key] = ts.ScriptTarget.ES3;
- break;
- case "es5":
- out[key] = ts.ScriptTarget.ES5;
- break;
- case "es6":
- case "es2015":
- out[key] = ts.ScriptTarget.ES2015;
- break;
- case "es2016":
- out[key] = ts.ScriptTarget.ES2016;
- break;
- case "es2017":
- out[key] = ts.ScriptTarget.ES2017;
- break;
- case "es2018":
- out[key] = ts.ScriptTarget.ES2018;
- break;
- case "es2019":
- out[key] = ts.ScriptTarget.ES2019;
- break;
- case "es2020":
- out[key] = ts.ScriptTarget.ES2020;
- break;
- case "esnext":
- out[key] = ts.ScriptTarget.ESNext;
- break;
- default:
- throw new TypeError("Unexpected emit target.");
- }
- break;
- case "types":
- const types = options[key];
- assert(types);
- files.push(...types);
- break;
- default:
- out[key] = options[key];
- }
- }
- return {
- options: out as ts.CompilerOptions,
- files: files.length ? files : undefined
- };
-}
-
-/** An array of TypeScript diagnostic types we ignore. */
-export const ignoredDiagnostics = [
- // TS2306: File 'file:///Users/rld/src/deno/cli/tests/subdir/amd_like.js' is
- // not a module.
- 2306,
- // TS1375: 'await' expressions are only allowed at the top level of a file
- // when that file is a module, but this file has no imports or exports.
- // Consider adding an empty 'export {}' to make this file a module.
- 1375,
- // TS1103: 'for-await-of' statement is only allowed within an async function
- // or async generator.
- 1103,
- // TS2691: An import path cannot end with a '.ts' extension. Consider
- // importing 'bad-module' instead.
- 2691,
- // TS5009: Cannot find the common subdirectory path for the input files.
- 5009,
- // TS5055: Cannot write file
- // 'http://localhost:4545/cli/tests/subdir/mt_application_x_javascript.j4.js'
- // because it would overwrite input file.
- 5055,
- // TypeScript is overly opinionated that only CommonJS modules kinds can
- // support JSON imports. Allegedly this was fixed in
- // Microsoft/TypeScript#26825 but that doesn't seem to be working here,
- // so we will ignore complaints about this compiler setting.
- 5070,
- // TS7016: Could not find a declaration file for module '...'. '...'
- // implicitly has an 'any' type. This is due to `allowJs` being off by
- // default but importing of a JavaScript module.
- 7016
-];
-
-/** When doing a host configuration, processing the response and logging out
- * and options which were ignored. */
-export function processConfigureResponse(
- configResult: ConfigureResponse,
- configPath: string
-): ts.Diagnostic[] | undefined {
- const { ignoredOptions, diagnostics } = configResult;
- if (ignoredOptions) {
- console.warn(
- yellow(`Unsupported compiler options in "${configPath}"\n`) +
- cyan(` The following options were ignored:\n`) +
- ` ${ignoredOptions.map((value): string => bold(value)).join(", ")}`
- );
- }
- return diagnostics;
-}
-
-// Constants used by `normalizeString` and `resolvePath`
-export const CHAR_DOT = 46; /* . */
-export const CHAR_FORWARD_SLASH = 47; /* / */
-
-/** Resolves `.` and `..` elements in a path with directory names */
-export function normalizeString(
- path: string,
- allowAboveRoot: boolean,
- separator: string,
- isPathSeparator: (code: number) => boolean
-): string {
- let res = "";
- let lastSegmentLength = 0;
- let lastSlash = -1;
- let dots = 0;
- let code: number;
- for (let i = 0, len = path.length; i <= len; ++i) {
- if (i < len) code = path.charCodeAt(i);
- else if (isPathSeparator(code!)) break;
- else code = CHAR_FORWARD_SLASH;
-
- if (isPathSeparator(code)) {
- if (lastSlash === i - 1 || dots === 1) {
- // NOOP
- } else if (lastSlash !== i - 1 && dots === 2) {
- if (
- res.length < 2 ||
- lastSegmentLength !== 2 ||
- res.charCodeAt(res.length - 1) !== CHAR_DOT ||
- res.charCodeAt(res.length - 2) !== CHAR_DOT
- ) {
- if (res.length > 2) {
- const lastSlashIndex = res.lastIndexOf(separator);
- if (lastSlashIndex === -1) {
- res = "";
- lastSegmentLength = 0;
- } else {
- res = res.slice(0, lastSlashIndex);
- lastSegmentLength = res.length - 1 - res.lastIndexOf(separator);
- }
- lastSlash = i;
- dots = 0;
- continue;
- } else if (res.length === 2 || res.length === 1) {
- res = "";
- lastSegmentLength = 0;
- lastSlash = i;
- dots = 0;
- continue;
- }
- }
- if (allowAboveRoot) {
- if (res.length > 0) res += `${separator}..`;
- else res = "..";
- lastSegmentLength = 2;
- }
- } else {
- if (res.length > 0) res += separator + path.slice(lastSlash + 1, i);
- else res = path.slice(lastSlash + 1, i);
- lastSegmentLength = i - lastSlash - 1;
- }
- lastSlash = i;
- dots = 0;
- } else if (code === CHAR_DOT && dots !== -1) {
- ++dots;
- } else {
- dots = -1;
- }
- }
- return res;
-}
-
-/** Return the common path shared by the `paths`.
- *
- * @param paths The set of paths to compare.
- * @param sep An optional separator to use. Defaults to `/`.
- * @internal
- */
-export function commonPath(paths: string[], sep = "/"): string {
- const [first = "", ...remaining] = paths;
- if (first === "" || remaining.length === 0) {
- return first.substring(0, first.lastIndexOf(sep) + 1);
- }
- const parts = first.split(sep);
-
- let endOfPrefix = parts.length;
- for (const path of remaining) {
- const compare = path.split(sep);
- for (let i = 0; i < endOfPrefix; i++) {
- if (compare[i] !== parts[i]) {
- endOfPrefix = i;
- }
- }
-
- if (endOfPrefix === 0) {
- return "";
- }
- }
- const prefix = parts.slice(0, endOfPrefix).join(sep);
- return prefix.endsWith(sep) ? prefix : `${prefix}${sep}`;
-}
-
-/** Utility function to turn the number of bytes into a human readable
- * unit */
-function humanFileSize(bytes: number): string {
- const thresh = 1000;
- if (Math.abs(bytes) < thresh) {
- return bytes + " B";
- }
- const units = ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
- let u = -1;
- do {
- bytes /= thresh;
- ++u;
- } while (Math.abs(bytes) >= thresh && u < units.length - 1);
- return `${bytes.toFixed(1)} ${units[u]}`;
-}
-
-// @internal
-export function base64ToUint8Array(data: string): Uint8Array {
- const binString = atob(data);
- const size = binString.length;
- const bytes = new Uint8Array(size);
- for (let i = 0; i < size; i++) {
- bytes[i] = binString.charCodeAt(i);
- }
- return bytes;
-}