UNPKG

@web/dev-server-esbuild

Version:
149 lines 6.69 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EsbuildPlugin = void 0; const dev_server_core_1 = require("@web/dev-server-core"); const esbuild_1 = require("esbuild"); const util_1 = require("util"); const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); const dom5_1 = require("@web/dev-server-core/dist/dom5"); const parse5_1 = require("parse5"); const getEsbuildTarget_js_1 = require("./getEsbuildTarget.js"); const filteredWarnings = ['Unsupported source map comment']; async function fileExists(path) { try { await (0, util_1.promisify)(fs_1.default.access)(path); return true; } catch (_a) { return false; } } class EsbuildPlugin { constructor(esbuildConfig) { this.transformedHtmlFiles = []; this.name = 'esbuild'; this.esbuildConfig = esbuildConfig; } async serverStart({ config, logger }) { this.config = config; this.logger = logger; if (this.esbuildConfig.tsconfig) { this.tsconfigRaw = await (0, util_1.promisify)(fs_1.default.readFile)(this.esbuildConfig.tsconfig, 'utf8'); } } resolveMimeType(context) { const fileExtension = path_1.default.posix.extname(context.path); if (this.esbuildConfig.handledExtensions.includes(fileExtension)) { return 'js'; } } async resolveImport({ source, context }) { const fileExtension = path_1.default.posix.extname(context.path); if (!this.esbuildConfig.tsFileExtensions.includes(fileExtension)) { // only handle typescript files return; } if (!source.endsWith('.js') || !source.startsWith('.')) { // only handle relative imports return; } // a TS file imported a .js file relatively, but they might intend to import a .ts file instead // check if the .ts file exists, and rewrite it in that case const filePath = (0, dev_server_core_1.getRequestFilePath)(context.url, this.config.rootDir); const fileDir = path_1.default.dirname(filePath); const importAsTs = source.substring(0, source.length - 3) + '.ts'; const importedTsFilePath = path_1.default.join(fileDir, importAsTs); if (!(await fileExists(importedTsFilePath))) { return; } return importAsTs; } transformCacheKey(context) { // the transformed files are cached per esbuild transform target const target = (0, getEsbuildTarget_js_1.getEsbuildTarget)(this.esbuildConfig.target, context.headers['user-agent']); return Array.isArray(target) ? target.join('_') : target; } async transform(context) { let loader; if (context.response.is('html')) { // we are transforming inline scripts loader = 'js'; } else { const fileExtension = path_1.default.posix.extname(context.path); loader = this.esbuildConfig.loaders[fileExtension]; } if (!loader) { // we are not handling this file return; } const target = (0, getEsbuildTarget_js_1.getEsbuildTarget)(this.esbuildConfig.target, context.headers['user-agent']); if (target === 'esnext' && loader === 'js') { // no need run esbuild, this happens when compile target is set to auto and the user is on a modern browser return; } const filePath = (0, dev_server_core_1.getRequestFilePath)(context.url, this.config.rootDir); if (context.response.is('html')) { this.transformedHtmlFiles.push(context.path); return this.__transformHtml(context, filePath, loader, target); } return this.__transformCode(context.body, filePath, loader, target); } async __transformHtml(context, filePath, loader, target) { const documentAst = (0, parse5_1.parse)(context.body); const inlineScripts = (0, dom5_1.queryAll)(documentAst, dom5_1.predicates.AND(dom5_1.predicates.hasTagName('script'), dom5_1.predicates.NOT(dom5_1.predicates.hasAttr('src')), dom5_1.predicates.OR(dom5_1.predicates.NOT(dom5_1.predicates.hasAttr('type')), dom5_1.predicates.hasAttrValue('type', 'module')))); if (inlineScripts.length === 0) { return; } for (const node of inlineScripts) { const code = (0, dom5_1.getTextContent)(node); const transformedCode = await this.__transformCode(code, filePath, loader, target); (0, dom5_1.setTextContent)(node, transformedCode); } return (0, parse5_1.serialize)(documentAst); } async __transformCode(code, filePath, loader, target) { try { const transformOptions = { sourcefile: filePath, sourcemap: 'inline', loader, target, // don't set any format for JS-like formats, otherwise esbuild reformats the code unnecessarily format: ['js', 'jsx', 'ts', 'tsx'].includes(loader) ? undefined : 'esm', jsxFactory: this.esbuildConfig.jsxFactory, jsxFragment: this.esbuildConfig.jsxFragment, define: this.esbuildConfig.define, tsconfigRaw: this.tsconfigRaw, banner: this.esbuildConfig.banner, footer: this.esbuildConfig.footer, }; const { code: transformedCode, warnings } = await (0, esbuild_1.transform)(code, transformOptions); if (warnings) { const relativePath = path_1.default.relative(process.cwd(), filePath); for (const warning of warnings) { if (!filteredWarnings.some(w => warning.text.includes(w))) { this.logger.warn(`[@web/test-runner-esbuild] Warning while transforming ${relativePath}: ${warning.text}`); } } } return transformedCode; } catch (e) { if (Array.isArray(e.errors)) { const msg = e.errors[0]; if (msg.location) { throw new dev_server_core_1.PluginSyntaxError(msg.text, filePath, code, msg.location.line, msg.location.column); } throw new Error(msg.text); } throw e; } } } exports.EsbuildPlugin = EsbuildPlugin; //# sourceMappingURL=EsbuildPlugin.js.map