UNPKG

@typed/content-hash

Version:

Content hash a directory of HTML/JS/CSS files and other static assets

161 lines 8.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createJavascriptPlugin = void 0; const tslib_1 = require("tslib"); const E = (0, tslib_1.__importStar)(require("@typed/fp/Env")); const FxEnv_1 = require("@typed/fp/FxEnv"); const builtin_modules_1 = (0, tslib_1.__importDefault)(require("builtin-modules")); const function_1 = require("fp-ts/function"); const Option_1 = require("fp-ts/Option"); const ReadonlyArray_1 = require("fp-ts/ReadonlyArray"); const path_1 = require("path"); const ts_morph_1 = require("ts-morph"); const typed_colors_1 = require("typed-colors"); const typescript_1 = require("typescript"); const logging_1 = require("../../application/services/logging"); const dependencyEq_1 = require("../dependencyEq"); const ensureRelative_1 = require("../ensureRelative"); const fsReadFile_1 = require("../fsReadFile"); const getHashFor_1 = require("../hashes/getHashFor"); const defaults_1 = require("./defaults"); const getFileExtension_1 = require("./getFileExtension"); const resolvePathFromSourceFile_1 = require("./resolvePathFromSourceFile"); const resolveTsConfigPaths_1 = require("./resolveTsConfigPaths"); const specifiersToSkip = [...builtin_modules_1.default, 'tslib']; const multiSeparatedExtensions = ['.proxy.js', '.d.ts.map', '.js.map', '.d.ts']; const simpleExtensions = ['.js', '.ts', '.jsx', '.tsx']; const supportedExtensions = [...multiSeparatedExtensions, ...simpleExtensions]; const quotes = [`'`, `"`]; const stripSpecifier = (s) => stripPrefix(stripPostfix(s)); const stripPrefix = (s) => (quotes.includes(s[0]) ? s.slice(1) : s); const stripPostfix = (s) => { const length = s.length; const lastIndex = length - 1; const end = s[lastIndex]; if (quotes.includes(end)) { return s.slice(0, lastIndex); } return s; }; const EXTENSIONLESS_EXTENSIONS = { '.js': ['.js', '.jsx'], '.jsx': ['.jsx', '.js'], '.d.ts': ['.d.ts', '.ts', '.js'], }; const getExtensions = (extension) => { if (extension in EXTENSIONLESS_EXTENSIONS) { return EXTENSIONLESS_EXTENSIONS[extension]; } return [extension]; }; const getProxyReplacementExt = (ext) => { const base = ext.slice(0, -9); if (base.endsWith('.map')) { return base.slice(0, -4); } return base; }; const createJavascriptPlugin = (options) => { const { compilerOptions = (0, typescript_1.getDefaultCompilerOptions)(), mainFields = defaults_1.MAIN_FIELDS } = options; const pathsResolver = (0, resolveTsConfigPaths_1.createResolveTsConfigPaths)({ compilerOptions }); const project = new ts_morph_1.Project({ compilerOptions: { ...compilerOptions, allowJs: true, }, skipAddingFilesFromTsConfig: true, skipFileDependencyResolution: true, useInMemoryFileSystem: true, }); const javascript = { readFilePath: (filePath) => (0, FxEnv_1.Do)(function* (_) { const ext = (0, getFileExtension_1.getFileExtension)(filePath); if (!supportedExtensions.some((se) => ext.endsWith(se))) { yield* _((0, logging_1.debug)(`${(0, typed_colors_1.red)(`[JS]`)} Unsupported file extension ${filePath}`)); return Option_1.none; } const shouldUseHashFor = multiSeparatedExtensions.some((se) => ext.endsWith(se)); const isProxyJs = ext.endsWith('.proxy.js'); yield* _((0, logging_1.debug)(`${(0, typed_colors_1.yellow)(`[JS]`)} Reading ${filePath}...`)); const initial = yield* _((0, fsReadFile_1.fsReadFile)(filePath, { supportsSourceMaps: !isProxyJs, isBase64Encoded: false })); const document = shouldUseHashFor ? (0, getHashFor_1.getHashFor)(initial, isProxyJs ? getProxyReplacementExt(ext) : '.js') : initial; yield* _((0, logging_1.debug)(`${(0, typed_colors_1.yellow)(`[JS]`)} Finding dependencies ${filePath}...`)); return (0, Option_1.some)(yield* _(findDependencies(project, pathsResolver, mainFields, document))); }), }; return javascript; }; exports.createJavascriptPlugin = createJavascriptPlugin; function findDependencies(project, pathsResolver, mainFields, document) { const contents = document.contents; const sourceFile = project.getSourceFile(document.filePath) || project.createSourceFile(document.filePath, contents); const sourceFilePath = sourceFile.getFilePath(); const extension = (0, getFileExtension_1.getFileExtension)(sourceFilePath); const hasServiceWorkerRegister = contents.includes('serviceWorker.register'); const standardStringLiterals = [ ...sourceFile.getImportStringLiterals(), ...sourceFile .getExportDeclarations() .map((d) => d.getModuleSpecifier()) .filter((x) => x !== undefined), ...(hasServiceWorkerRegister ? findServiceWorkerRegister(sourceFile) : []), ]; const absoluteStringLiterals = [ ...(extension.endsWith('.proxy.js') ? sourceFile.getExportAssignments().flatMap((a) => a.getDescendantsOfKind(ts_morph_1.SyntaxKind.StringLiteral)) : []), ]; const stringLiterals = [ ...standardStringLiterals.map((s) => [s, false]), ...absoluteStringLiterals.map((s) => [s, true]), ]; return (0, function_1.pipe)(stringLiterals.map(([literal, useBaseName]) => { const specifier = stripSpecifier(literal.getText()); const moduleSpecifier = useBaseName ? (0, ensureRelative_1.ensureRelative)(path_1.posix.basename(specifier)) : specifier; if (specifiersToSkip.includes(moduleSpecifier)) { return E.of(Option_1.none); } return (0, function_1.pipe)((0, resolvePathFromSourceFile_1.resolvePathFromSourceFile)({ moduleSpecifier, directory: path_1.posix.dirname(sourceFilePath), pathsResolver, extensions: getExtensions(extension), mainFields, }), E.map((0, Option_1.map)((filePath) => { const start = literal.getStart() + 1; const end = literal.getEnd() - 1; const dep = { specifier, filePath, fileExtension: (0, getFileExtension_1.getFileExtension)(filePath), position: { start, end }, }; return dep; }))); }), E.zip, E.map((dependencies) => ({ ...document, dependencies: (0, function_1.pipe)(dependencies.filter(Option_1.isSome).map((o) => o.value), (0, ReadonlyArray_1.uniq)(dependencyEq_1.dependencyEq)), }))); } function findServiceWorkerRegister(sourceFile) { const literals = []; const callExpressions = sourceFile.getStatements().flatMap((s) => s.getChildrenOfKind(ts_morph_1.SyntaxKind.CallExpression)); callExpressions.forEach((callExpression) => { var _a, _b, _c; const firstAccess = callExpression.getFirstDescendantByKind(ts_morph_1.SyntaxKind.PropertyAccessExpression); const secondaryAccess = firstAccess === null || firstAccess === void 0 ? void 0 : firstAccess.getFirstDescendantByKind(ts_morph_1.SyntaxKind.PropertyAccessExpression); const firstIdentifier = (_a = firstAccess === null || firstAccess === void 0 ? void 0 : firstAccess.getLastChildByKind(ts_morph_1.SyntaxKind.Identifier)) === null || _a === void 0 ? void 0 : _a.getText(); const secondaryIdentifier = secondaryAccess ? (_b = secondaryAccess.getLastChildByKind(ts_morph_1.SyntaxKind.Identifier)) === null || _b === void 0 ? void 0 : _b.getText() : (_c = firstAccess === null || firstAccess === void 0 ? void 0 : firstAccess.getFirstChildByKind(ts_morph_1.SyntaxKind.Identifier)) === null || _c === void 0 ? void 0 : _c.getText(); if (firstIdentifier === 'register' && secondaryIdentifier === 'serviceWorker') { literals.push(...callExpression .getChildrenOfKind(ts_morph_1.SyntaxKind.SyntaxList) .flatMap((l) => l.getChildrenOfKind(ts_morph_1.SyntaxKind.StringLiteral))); } }); return literals; } //# sourceMappingURL=javascript.js.map