diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2020-03-11 10:53:06 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-11 10:53:06 +0100 |
commit | 99a0c6df79b903e4fe72ce066787039bdede3868 (patch) | |
tree | 1c0abdb964a2e052b593dc8fa3e515f76dbc0642 /cli/js/compiler/imports.ts | |
parent | 94f4c6807a34a564f567b984e71e36e9cb9c5005 (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/imports.ts')
-rw-r--r-- | cli/js/compiler/imports.ts | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/cli/js/compiler/imports.ts b/cli/js/compiler/imports.ts new file mode 100644 index 000000000..077303b61 --- /dev/null +++ b/cli/js/compiler/imports.ts @@ -0,0 +1,183 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +import { MediaType, SourceFile, SourceFileJson } from "./sourcefile.ts"; +import { normalizeString, CHAR_FORWARD_SLASH } from "./util.ts"; +import { cwd } from "../ops/fs/dir.ts"; +import { assert } from "../util.ts"; +import * as util from "../util.ts"; +import * as compilerOps from "../ops/compiler.ts"; + +/** Resolve a path to the final path segment passed. */ +function resolvePath(...pathSegments: string[]): string { + let resolvedPath = ""; + let resolvedAbsolute = false; + + for (let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + let path: string; + + if (i >= 0) path = pathSegments[i]; + else path = cwd(); + + // Skip empty entries + if (path.length === 0) { + continue; + } + + resolvedPath = `${path}/${resolvedPath}`; + resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when cwd() fails) + + // Normalize the path + resolvedPath = normalizeString( + resolvedPath, + !resolvedAbsolute, + "/", + code => code === CHAR_FORWARD_SLASH + ); + + if (resolvedAbsolute) { + if (resolvedPath.length > 0) return `/${resolvedPath}`; + else return "/"; + } else if (resolvedPath.length > 0) return resolvedPath; + else return "."; +} + +/** Resolve a relative specifier based on the referrer. Used when resolving + * modules internally within the runtime compiler API. */ +function resolveSpecifier(specifier: string, referrer: string): string { + if (!specifier.startsWith(".")) { + return specifier; + } + const pathParts = referrer.split("/"); + pathParts.pop(); + let path = pathParts.join("/"); + path = path.endsWith("/") ? path : `${path}/`; + return resolvePath(path, specifier); +} + +/** Ops to Rust to resolve modules' URLs. */ +export function resolveModules( + specifiers: string[], + referrer?: string +): string[] { + util.log("compiler_imports::resolveModules", { specifiers, referrer }); + return compilerOps.resolveModules(specifiers, referrer); +} + +/** Ops to Rust to fetch modules meta data. */ +function fetchSourceFiles( + specifiers: string[], + referrer?: string +): Promise<SourceFileJson[]> { + util.log("compiler_imports::fetchSourceFiles", { specifiers, referrer }); + return compilerOps.fetchSourceFiles(specifiers, referrer); +} + +/** Given a filename, determine the media type based on extension. Used when + * resolving modules internally in a runtime compile. */ +function getMediaType(filename: string): MediaType { + const maybeExtension = /\.([a-zA-Z]+)$/.exec(filename); + if (!maybeExtension) { + util.log(`!!! Could not identify valid extension: "${filename}"`); + return MediaType.Unknown; + } + const [, extension] = maybeExtension; + switch (extension.toLowerCase()) { + case "js": + return MediaType.JavaScript; + case "jsx": + return MediaType.JSX; + case "json": + return MediaType.Json; + case "ts": + return MediaType.TypeScript; + case "tsx": + return MediaType.TSX; + case "wasm": + return MediaType.Wasm; + default: + util.log(`!!! Unknown extension: "${extension}"`); + return MediaType.Unknown; + } +} + +/** Recursively process the imports of modules from within the supplied sources, + * generating `SourceFile`s of any imported files. + * + * Specifiers are supplied in an array of tuples where the first is the + * specifier that will be requested in the code and the second is the specifier + * that should be actually resolved. */ +export function processLocalImports( + sources: Record<string, string>, + specifiers: Array<[string, string]>, + referrer?: string, + processJsImports = false +): string[] { + if (!specifiers.length) { + return []; + } + const moduleNames = specifiers.map( + referrer + ? ([, specifier]): string => resolveSpecifier(specifier, referrer) + : ([, specifier]): string => specifier + ); + for (let i = 0; i < moduleNames.length; i++) { + const moduleName = moduleNames[i]; + assert(moduleName in sources, `Missing module in sources: "${moduleName}"`); + const sourceFile = + SourceFile.get(moduleName) || + new SourceFile({ + url: moduleName, + filename: moduleName, + sourceCode: sources[moduleName], + mediaType: getMediaType(moduleName) + }); + sourceFile.cache(specifiers[i][0], referrer); + if (!sourceFile.processed) { + processLocalImports( + sources, + sourceFile.imports(processJsImports), + sourceFile.url, + processJsImports + ); + } + } + return moduleNames; +} + +/** Recursively process the imports of modules, generating `SourceFile`s of any + * imported files. + * + * Specifiers are supplied in an array of tuples where the first is the + * specifier that will be requested in the code and the second is the specifier + * that should be actually resolved. */ +export async function processImports( + specifiers: Array<[string, string]>, + referrer?: string, + processJsImports = false +): Promise<string[]> { + if (!specifiers.length) { + return []; + } + const sources = specifiers.map(([, moduleSpecifier]) => moduleSpecifier); + const resolvedSources = resolveModules(sources, referrer); + const sourceFiles = await fetchSourceFiles(resolvedSources, referrer); + assert(sourceFiles.length === specifiers.length); + for (let i = 0; i < sourceFiles.length; i++) { + const sourceFileJson = sourceFiles[i]; + const sourceFile = + SourceFile.get(sourceFileJson.url) || new SourceFile(sourceFileJson); + sourceFile.cache(specifiers[i][0], referrer); + if (!sourceFile.processed) { + await processImports( + sourceFile.imports(processJsImports), + sourceFile.url, + processJsImports + ); + } + } + return resolvedSources; +} |