UNPKG

marko

Version:

UI Components + streaming, async, high performance, HTML templating for Node.js and the browser.

372 lines (326 loc) • 11.3 kB
"use strict";var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");exports.__esModule = true;exports.entryBuilder = exports.default = void 0;var _compiler = require("@marko/compiler"); var _babelUtils = require("@marko/compiler/babel-utils"); var _modules = _interopRequireDefault(require("@marko/compiler/modules")); var _magicString = _interopRequireDefault(require("magic-string")); var _path = _interopRequireDefault(require("path")); const kEntryState = Symbol(); const lassoDepPrefix = "package: ";var _default = (entryFile, isHydrate) => { const program = entryFile.path; const programNode = program.node; if (!isHydrate) { const body = []; addBrowserImports(new Set(), undefined, body, entryFile, entryFile); if (body.length) { programNode.body = body.concat(programNode.body); } return; } const visitedFiles = new Set([ (0, _babelUtils.resolveRelativePath)(entryFile, entryFile.opts.filename)] ); entryBuilder.visit(entryFile, entryFile, function visitChild(resolved) { if (!visitedFiles.has(resolved)) { visitedFiles.add(resolved); const file = (0, _babelUtils.loadFileForImport)(entryFile, resolved); if (file) { entryBuilder.visit(file, entryFile, (id) => visitChild(resolveRelativeToEntry(entryFile, file, id)) ); } } }); programNode.body = entryBuilder.build(entryFile); program.skip(); };exports.default = _default; const entryBuilder = exports.entryBuilder = { build(entryFile, exportInit) { const state = entryFile[kEntryState]; if (!state) { throw entryFile.path.buildCodeFrameError( "Unable to build hydrate code, no files were visited before finalizing the build" ); } const { markoOpts } = entryFile; const entryMarkoMeta = entryFile.metadata.marko; const { body } = state; let didExportInit = false; entryMarkoMeta.watchFiles = [...state.watchFiles]; entryMarkoMeta.deps = [...state.lassoDeps]; if (state.hasComponents) { const initId = _compiler.types.identifier("init"); const markoComponentsImport = importPath( (0, _babelUtils.resolveRelativePath)(entryFile, "marko/src/runtime/components/index.js") ); if (state.splitComponentIndex) { markoComponentsImport.specifiers.push( _compiler.types.importSpecifier(_compiler.types.identifier("register"), _compiler.types.identifier("register")) ); } body.unshift(markoComponentsImport); if (markoOpts.hydrateInit || exportInit) { const initExpression = _compiler.types.callExpression( initId, markoOpts.runtimeId ? [_compiler.types.stringLiteral(markoOpts.runtimeId)] : [] ); markoComponentsImport.specifiers.push( _compiler.types.importSpecifier(initId, initId) ); body.push( exportInit ? _compiler.types.exportDefaultDeclaration( _compiler.types.arrowFunctionExpression([], initExpression) ) : _compiler.types.expressionStatement(initExpression) ); } } else if (exportInit) { body.push( _compiler.types.exportDefaultDeclaration( _compiler.types.arrowFunctionExpression([], _compiler.types.blockStatement([])) ) ); } return body; }, visit(file, entryFile, visitChild) { const fileMeta = file.metadata.marko; const fileName = file.opts.filename; const state = entryFile[kEntryState] ||= { shouldIncludeImport: toTestFn(file.markoOpts.hydrateIncludeImports), watchFiles: new Set(), imports: new Set(), lassoDeps: new Set(), hasComponents: false, splitComponentIndex: 0, body: [] }; const { watchFiles, imports, lassoDeps, body } = state; if (fileMeta.component) { state.hasComponents = true; if (_path.default.basename(fileMeta.component) === _path.default.basename(fileName)) { // Stateful component. addImport(imports, body, (0, _babelUtils.resolveRelativePath)(entryFile, fileName)); return; } } watchFiles.add(fileName); for (const watchFile of fileMeta.watchFiles) { watchFiles.add(watchFile); } addBrowserImports(imports, lassoDeps, body, file, entryFile); for (const child of file.path.node.body) { if (_compiler.types.isImportDeclaration(child)) { const { value } = child.source; if (state.shouldIncludeImport(value)) { addImport( imports, body, resolveRelativeToEntry(entryFile, file, value) ); } } else if ( _compiler.types.isMarkoScriptlet(child) && child.static && child.target !== "server") { for (const stmt of child.body) { if (_compiler.types.isImportDeclaration(stmt)) { const { value } = stmt.source; if (child.target === "client" || state.shouldIncludeImport(value)) { addImport( imports, body, resolveRelativeToEntry(entryFile, file, value) ); } } } } } for (const tag of fileMeta.tags) { if (tag.endsWith(".marko")) { visitChild(tag); } else if (/^@lasso\/marko-taglib\//.test(tag)) { state.hasComponents = true; } else { const importedTemplates = tryGetTemplateImports(file, tag); if (importedTemplates) { for (const templateFile of importedTemplates) { visitChild(templateFile); } } } } if (fileMeta.component) { // Split component const splitComponentId = _compiler.types.identifier( `component_${state.splitComponentIndex++}` ); const splitComponentImport = importPath( resolveRelativeToEntry(entryFile, file, fileMeta.component) ); splitComponentImport.specifiers.push( _compiler.types.importDefaultSpecifier(splitComponentId) ); body.push( splitComponentImport, _compiler.types.expressionStatement( _compiler.types.callExpression(_compiler.types.identifier("register"), [ _compiler.types.stringLiteral(fileMeta.id), splitComponentId] ) ) ); } } }; function addBrowserImports(seenImports, lassoDeps, body, file, entryFile) { const { filename, sourceMaps } = file.opts; let s; for (let dep of file.metadata.marko.deps) { if (typeof dep !== "string") { const { virtualPath } = dep; let { code } = dep; let map; if (sourceMaps && dep.startPos !== undefined) { s = s || new _magicString.default(file.code, { filename }); map = s.snip(dep.startPos, dep.endPos).generateMap({ source: filename, includeContent: true }); if (sourceMaps === "inline" || sourceMaps === "both") { code += dep.style ? `\n/*# sourceMappingURL=${map.toUrl()}*/` : `\n//# sourceMappingURL=${map.toUrl()}`; if (sourceMaps === "inline") { map = undefined; } } } dep = file.markoOpts.resolveVirtualDependency(filename, { map, code, virtualPath }); if (!dep) { continue; } } else if (isLassoDep(dep)) { if (lassoDeps) { lassoDeps.add( resolveLassoManifestDepRelativeToEntry(entryFile, file, dep) ); } continue; } addImport(seenImports, body, resolveRelativeToEntry(entryFile, file, dep)); } } function addImport(seenImports, body, resolved) { if (seenImports.has(resolved)) return; seenImports.add(resolved); body.push(importPath(resolved)); } function resolveRelativeToEntry(entryFile, file, req) { return file === entryFile ? (0, _babelUtils.resolveRelativePath)(file, req) : (0, _babelUtils.resolveRelativePath)( entryFile, req[0] === "." ? _path.default.join(file.opts.filename, "..", req) : req ); } function importPath(path) { return _compiler.types.importDeclaration([], _compiler.types.stringLiteral(path)); } function tryGetTemplateImports(file, rendererRelativePath) { const resolvedRendererPath = tryResolveFrom( rendererRelativePath, file.opts.filename ); let templateImports; if (resolvedRendererPath) { if (resolvedRendererPath.endsWith(".marko")) { addTemplateImport(resolvedRendererPath); } else if (resolvedRendererPath.endsWith(".js")) { try { for (const statement of (0, _babelUtils.parseStatements)( file, file.markoOpts.fileSystem.readFileSync(resolvedRendererPath, "utf-8") )) { if (statement.type === "ImportDeclaration") { addTemplateImport(statement.source.value); } else { _compiler.types.traverseFast(statement, (node) => { if ( node.type === "CallExpression" && ( node.callee.name === "require" || node.callee.type === "MemberExpression" && node.callee.object.type === "Identifier" && node.callee.object.name === "require" && node.callee.property.type === "Identifier" && node.callee.property.name === "resolve") && node.arguments.length === 1 && node.arguments[0].type === "StringLiteral") { addTemplateImport(node.arguments[0].value); } }); } } } catch { // Ignore }} } return templateImports; function addTemplateImport(request) { if (request.endsWith(".marko")) { const resolvedTemplatePath = tryResolveFrom( request, resolvedRendererPath ); if (resolvedTemplatePath) { if (templateImports) { templateImports.push(resolvedTemplatePath); } else { templateImports = [resolvedTemplatePath]; } } } } } function tryResolveFrom(request, from) { return request[0] === "." ? _path.default.resolve(from, "..", request) : /^(?:[/\\]|[a-zA-Z]:)/.test(request) ? request : _modules.default.tryResolve(request, _path.default.dirname(from)); } function toTestFn(val) { if (typeof val === "function") { return val; } return val.test.bind(val); } function isLassoDep(dep) { return dep.startsWith(lassoDepPrefix); } /** * Lasso manifest deps in `lasso-marko` do not support node module resolution. * eg `package: @ebay/ebayui-core/browser.json` will not resolve. * * This resolution returns a direct posix relative path. */ function resolveLassoManifestDepRelativeToEntry(entryFile, targetFile, dep) { const entryFileName = entryFile.opts.filename; const targetFileName = targetFile.opts.filename; if (entryFileName === targetFileName) return dep; const resolved = toPosix( _path.default.relative( _path.default.dirname(entryFileName), _path.default.join(targetFileName, "..", dep.slice(lassoDepPrefix.length)) ) ); return lassoDepPrefix + (resolved[0] === "." ? resolved : `./${resolved}`); } const toPosix = _path.default.sep === "/" ? (v) => v : (v) => v.replace(/\\/g, "/");