diff --git a/.changeset/mighty-ligers-beam.md b/.changeset/mighty-ligers-beam.md new file mode 100644 index 000000000..3c3fc74ef --- /dev/null +++ b/.changeset/mighty-ligers-beam.md @@ -0,0 +1,5 @@ +--- +'wmr': patch +--- + +Rewrite internal source map handling. This adds full support for source maps during `development` and `production` and ensures that `.map` files are served correctly. diff --git a/package.json b/package.json index 506a30833..63bc57c15 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "demo": "yarn workspace @examples/demo run", "docs": "yarn workspace docs", "iso": "yarn workspace preact-iso", - "ci": "yarn wmr build && yarn --check-files && yarn demo build:prod" + "ci": "yarn wmr build && yarn --check-files && yarn demo build:prod", + "postinstall": "patch-package --exclude 'nothing'" }, "eslintConfig": { "extends": [ @@ -91,6 +92,7 @@ "eslint-plugin-prettier": "^3.1.4", "husky": "^4.2.5", "lint-staged": "^10.2.11", + "patch-package": "^6.4.7", "prettier": "^2.0.5" } } diff --git a/packages/wmr/package.json b/packages/wmr/package.json index 5167e8cbd..7550ad189 100644 --- a/packages/wmr/package.json +++ b/packages/wmr/package.json @@ -82,6 +82,7 @@ "semver": "^7.3.2", "simple-code-frame": "^1.1.1", "sirv": "^1.0.6", + "sourcemap-codec": "^1.4.8", "stylis": "^4.0.10", "sucrase": "^3.17.0", "tar-stream": "^2.1.3", diff --git a/packages/wmr/src/lib/acorn-traverse.js b/packages/wmr/src/lib/acorn-traverse.js index 08b2ec00a..8e18ded57 100644 --- a/packages/wmr/src/lib/acorn-traverse.js +++ b/packages/wmr/src/lib/acorn-traverse.js @@ -3,6 +3,7 @@ import * as jsxWalk from 'acorn-jsx-walk'; import MagicString from 'magic-string'; import * as astringLib from 'astring'; import { codeFrame } from './output-utils.js'; +import { posix } from 'path'; /** * @fileoverview @@ -100,7 +101,16 @@ let codeGenerator = { // import(source) ImportExpression(node, state) { state.write('import('); - this[node.source.type](node.source, state); + + // TODO: Sometimes this seems to have a source and sometimes + // an expression. I don't understand why. The expression seems + // to be only set when calling `t.importExpression()` + if (node.source) { + this[node.source.type](node.source, state); + } else { + this[node.expression.type](node.expression, state); + } + state.write(')'); }, JSXFragment(node, state) { @@ -745,8 +755,10 @@ export function transform( function getSourceMap() { if (!map) { map = out.generateMap({ - includeContent: false, - source: sourceFileName + includeContent: true, + // Must be set for most source map verifiers to work + source: sourceFileName || filename, + file: posix.basename(sourceFileName || filename || '') }); } return map; diff --git a/packages/wmr/src/lib/normalize-options.js b/packages/wmr/src/lib/normalize-options.js index 0f6efe44a..7a840dbe2 100644 --- a/packages/wmr/src/lib/normalize-options.js +++ b/packages/wmr/src/lib/normalize-options.js @@ -21,7 +21,6 @@ export async function normalizeOptions(options, mode, configWatchFiles = []) { options.root = options.cwd; - options.sourcemap = false; options.minify = mode === 'build'; options.plugins = []; options.output = []; diff --git a/packages/wmr/src/lib/npm-middleware.js b/packages/wmr/src/lib/npm-middleware.js index d5724a6ab..d6b4808ac 100644 --- a/packages/wmr/src/lib/npm-middleware.js +++ b/packages/wmr/src/lib/npm-middleware.js @@ -142,6 +142,7 @@ async function bundleNpmModule(mod, { source, alias, cwd }) { aliasPlugin({ alias, cwd }), npmProviderPlugin, processGlobalPlugin({ + sourcemap: false, NODE_ENV: 'development' }), commonjs({ diff --git a/packages/wmr/src/lib/plugins.js b/packages/wmr/src/lib/plugins.js index 84cb8f862..76cb79d3f 100644 --- a/packages/wmr/src/lib/plugins.js +++ b/packages/wmr/src/lib/plugins.js @@ -53,7 +53,7 @@ export function getPlugins(options) { production }), // Transpile import assertion syntax to WMR prefixes - importAssertionPlugin(), + importAssertionPlugin({ sourcemap }), production && (dynamicImportVars.default || dynamicImportVars)({ include: /\.(m?jsx?|tsx?)$/, @@ -61,13 +61,14 @@ export function getPlugins(options) { }), production && publicPathPlugin({ publicPath }), sassPlugin({ production, sourcemap, root }), - wmrStylesPlugin({ hot: !production, root, production, alias }), + wmrStylesPlugin({ hot: !production, root, production, alias, sourcemap }), processGlobalPlugin({ + sourcemap, env, NODE_ENV: production ? 'production' : 'development' }), - htmPlugin({ production }), - wmrPlugin({ hot: !production, preact: features.preact }), + htmPlugin({ production, sourcemap: options.sourcemap }), + wmrPlugin({ hot: !production, preact: features.preact, sourcemap: options.sourcemap }), fastCjsPlugin({ // Only transpile CommonJS in node_modules and explicit .cjs files: include: /(^npm\/|[/\\]node_modules[/\\]|\.cjs$)/ diff --git a/packages/wmr/src/lib/rollup-plugin-container.js b/packages/wmr/src/lib/rollup-plugin-container.js index 16561912b..dcf1e8df8 100644 --- a/packages/wmr/src/lib/rollup-plugin-container.js +++ b/packages/wmr/src/lib/rollup-plugin-container.js @@ -3,7 +3,8 @@ import { createHash } from 'crypto'; import { promises as fs } from 'fs'; import * as acorn from 'acorn'; import * as kl from 'kolorist'; -import { debug, formatResolved, formatPath } from './output-utils.js'; +import { debug, formatResolved, formatPath, hasDebugFlag } from './output-utils.js'; +import { mergeSourceMaps } from './sourcemap.js'; // Rollup respects "module", Node 14 doesn't. const cjsDefault = m => ('default' in m ? m.default : m); @@ -45,7 +46,7 @@ function identifierPair(id, importer) { /** * @param {Plugin[]} plugins - * @param {import('rollup').InputOptions & PluginContainerOptions} [opts] + * @param {import('rollup').InputOptions & PluginContainerOptions & {sourcemap?: boolean}} [opts] */ export function createPluginContainer(plugins, opts = {}) { if (!Array.isArray(plugins)) plugins = [plugins]; @@ -279,6 +280,9 @@ export function createPluginContainer(plugins, opts = {}) { * @param {string} id */ async transform(code, id) { + /** @type {import('./sourcemap.js').SourceMap[]} */ + const sourceMaps = []; + for (plugin of plugins) { if (!plugin.transform) continue; const result = await plugin.transform.call(ctx, code, id); @@ -286,12 +290,32 @@ export function createPluginContainer(plugins, opts = {}) { logTransform(`${kl.dim(formatPath(id))} [${plugin.name}]`); if (typeof result === 'object') { + if (result.map) { + // Normalize source map sources URLs for the browser + result.map.sources = result.map.sources.map(s => { + if (typeof s === 'string') { + return `/${posix.normalize(s)}`; + } else if (hasDebugFlag()) { + logTransform(kl.yellow(`Invalid source map returned by plugin `) + kl.magenta(plugin.name)); + } + + return s; + }); + + sourceMaps.push(result.map); + } else if (opts.sourcemap && result.code !== code) { + logTransform(kl.yellow(`Missing sourcemap result in transform() method of `) + kl.magenta(plugin.name)); + } + code = result.code; } else { + if (opts.sourcemap && code !== result) { + logTransform(kl.yellow(`Missing sourcemap result in transform() method of `) + kl.magenta(plugin.name)); + } code = result; } } - return code; + return { code, map: sourceMaps.length ? mergeSourceMaps(sourceMaps) : null }; }, /** diff --git a/packages/wmr/src/lib/sourcemap.js b/packages/wmr/src/lib/sourcemap.js new file mode 100644 index 000000000..7d86609b0 --- /dev/null +++ b/packages/wmr/src/lib/sourcemap.js @@ -0,0 +1,185 @@ +import { encode, decode } from 'sourcemap-codec'; + +/** + * @typedef {{ version: number, file?: string, sourceRoot?: string, sources: string[], sourcesContent: Array, names: string[], mappings: string}} SourceMap + */ + +/** + * + * @param {SourceMap[]} maps + * @param {import('sourcemap-codec').SourceMapMappings[]} parsedMappings + * @param {Map} sourcesCache + * @param {number} source + * @param {number} line + * @param {number} column + * @returns {{ line: number, column: number, source: number }} + */ +function getOriginalPosition(maps, parsedMappings, sourcesCache, source, line, column) { + let originalLine = line; + let originalColumn = column; + let originalSource = source; + + // Traverse maps backwards, but skip last map as that's the + // one we requested the position from + for (let i = parsedMappings.length - 1; i >= 0; i--) { + const map = parsedMappings[i]; + + const segments = map[originalLine]; + if (!segments) break; + + // TODO: Binary search + let lastFound; + for (let j = 0; j < segments.length; j++) { + const segment = segments[j]; + if (segment[0] === originalColumn) { + if (segment.length === 1) { + break; + } + + const sourceName = maps[i].sources[segment[1]]; + const mappedName = sourcesCache.get(sourceName); + if (!mappedName) { + originalSource = 0; + } else { + originalSource = mappedName.index; + } + + originalLine = segment[2]; + originalColumn = segment[3]; + break; + } else if (segment[0] < originalColumn) { + lastFound = segment; + } + } + + if (lastFound !== undefined) { + if (lastFound.length === 1) { + break; + } + + const sourceName = maps[i].sources[lastFound[1]]; + const mappedName = sourcesCache.get(sourceName); + if (!mappedName) { + originalSource = 0; + } else { + originalSource = mappedName.index; + } + + originalLine = lastFound[2]; + originalColumn = lastFound[3] + (originalColumn - lastFound[3]); + } + } + + return { + line: originalLine, + column: originalColumn, + source: originalSource + }; +} + +/** + * Combine an array of sourcemaps into one + * @param {SourceMap[]} sourceMaps + * @returns {SourceMap} + */ +export function mergeSourceMaps(sourceMaps) { + let file; + let sourceRoot; + + /** @type {import('sourcemap-codec').SourceMapMappings[]} */ + const parsedMappings = []; + + /** @type {Map} */ + const names = new Map(); + + /** @type {Map} */ + const sourcesCache = new Map(); + + for (let i = 0; i < sourceMaps.length; i++) { + const map = sourceMaps[i]; + + if (map.file) { + file = map.file; + } + + if (map.sourceRoot) { + sourceRoot = map.sourceRoot; + } + + for (let j = 0; j < map.sources.length; j++) { + const source = map.sources[j]; + if (!sourcesCache.has(source)) { + sourcesCache.set(source, { index: sourcesCache.size, content: map.sourcesContent[j] || null }); + } + } + + for (let j = 0; j < map.names.length; j++) { + const name = map.names[j]; + if (!names.has(name)) { + names.set(name, names.size - 1); + } + } + + // Merge mappings + parsedMappings.push(decode(map.mappings)); + } + + const sources = []; + const sourcesContent = []; + for (const [key, value] of sourcesCache.entries()) { + sources.push(key); + sourcesContent.push(value.content); + } + + /** @type {import('sourcemap-codec').SourceMapMappings} */ + const outMappings = []; + const lastMap = parsedMappings[parsedMappings.length - 1]; + + // Loop over the mappings of the last source map and retrieve + // original position for each mapping segment + for (let i = 0; i < lastMap.length; i++) { + const line = lastMap[i]; + + /** @type {import('sourcemap-codec').SourceMapSegment[]} */ + const rewrittenSegments = []; + for (let j = 0; j < line.length; j++) { + const segment = line[j]; + + if (segment.length === 4) { + const original = getOriginalPosition( + sourceMaps, + parsedMappings, + sourcesCache, + segment[1], + segment[2], + segment[3] + ); + rewrittenSegments.push([segment[0], original.source, original.line, original.column]); + } else if (segment.length === 5) { + const original = getOriginalPosition( + sourceMaps, + parsedMappings, + sourcesCache, + segment[1], + segment[2], + segment[3] + ); + rewrittenSegments.push([segment[0], original.source, original.line, original.column, segment[4]]); + } else { + rewrittenSegments.push(segment); + } + } + + outMappings.push(rewrittenSegments); + } + + return { + version: 3, + file, + names: Array.from(names.keys()), + sourceRoot, + sources, + sourcesContent, + mappings: encode(outMappings) + }; +} diff --git a/packages/wmr/src/lib/transform-imports.js b/packages/wmr/src/lib/transform-imports.js index d1ef0316c..d2672ccdb 100644 --- a/packages/wmr/src/lib/transform-imports.js +++ b/packages/wmr/src/lib/transform-imports.js @@ -1,77 +1,65 @@ -import { parse } from 'es-module-lexer/dist/lexer.js'; +import { parse } from 'es-module-lexer'; +import MagicString from 'magic-string'; +import path from 'path'; /** @template T @typedef {Promise|T} MaybePromise */ /** @typedef {(specifier: string, id?: string) => MaybePromise} ResolveFn */ +const KIND_IMPORT = 0; +const KIND_DYNAMIC_IMPORT = 1; +const KIND_IMPORT_META = 2; + /** * @param {string} code Module code * @param {string} id Source module specifier - * @param {object} [options] + * @param {object} options * @param {ResolveFn?} [options.resolveImportMeta] Replace `import.meta.FIELD` with a JS string. Return `false`/`null` preserve. * @param {ResolveFn?} [options.resolveId] Return a replacement for import specifiers * @param {ResolveFn?} [options.resolveDynamicImport] `false` preserves, `null` falls back to resolveId() + * @param {boolean} [options.sourcemap] + * @returns {Promise<{ code: string, map: any }>} */ -export async function transformImports(code, id, { resolveImportMeta, resolveId, resolveDynamicImport } = {}) { +export async function transformImports(code, id, { resolveImportMeta, resolveId, resolveDynamicImport, sourcemap }) { const [imports] = await parse(code, id); - let out = ''; + const s = new MagicString(code); let offset = 0; - // Import specifiers are synchronously converted into placeholders. - // Resolutions are async+parallel, held in a mapping to their placeholders. - let resolveIds = 0; - const toResolve = new Map(); - - // Get a [deduplicated] placeholder for a specifier and kick off resolution - const field = (spec, resolver, a, b) => { - const match = toResolve.get(spec); - if (match) return match.placeholder; - const placeholder = `%%_RESOLVE_#${++resolveIds}#_%%`; - toResolve.set(spec, { - placeholder, - spec, - p: resolver(a, b) - }); - return placeholder; - }; - - // Falls through to resolveId() if null, preserves spec if false - const doResolveDynamicImport = async (spec, id) => { - let f = resolveDynamicImport && (await resolveDynamicImport(spec, id)); - if (f == null && resolveId) f = await resolveId(spec, id); - return f; - }; + /** @type {Array<{ spec: string, start: number, end: number, kind: number, property: null | string}>} */ + const toResolve = []; for (const item of imports) { // Skip items that were already processed by being wrapped in an import - eg `import(import.meta.url)` if (item.s < offset) continue; - out += code.substring(offset, item.s); - - const isImportMeta = item.d === -2; + let isImportMeta = item.d === -2; const isDynamicImport = item.d > -1; if (isDynamicImport) { // Bugfix: `import(import.meta.url)` returns an invalid negative end_offset. // We detect that here and find the closing paren to estimate the offset. if (item.e < 0) { + // @ts-ignore item.e = code.indexOf(')', item.s); + isImportMeta = true; } // dynamic import() has no statement_end, so we take the position following the closing paren: + // @ts-ignore item.se = code.indexOf(')', item.e) + 1; } let quote = ''; - let after = code.substring(item.e, item.se); let spec = code.substring(item.s, item.e); offset = item.se; // import.meta if (isImportMeta) { + offset = item.s + 'import.meta'.length; // Check for *simple* property access immediately following `import.meta`: const r = /\s*\.\s*([a-z_$][a-z0-9_$]*)/gi; r.lastIndex = offset; + const match = r.exec(code); if (match && match.index === offset) { // advance past it and append it to the "specifier": @@ -80,10 +68,9 @@ export async function transformImports(code, id, { resolveImportMeta, resolveId, // resolve it: const property = match[1]; if (resolveImportMeta) { - spec = field(spec, resolveImportMeta, property); + toResolve.push({ spec, start: item.s, end: offset, kind: KIND_IMPORT_META, property }); } } - out += spec; continue; } @@ -106,41 +93,66 @@ export async function transformImports(code, id, { resolveImportMeta, resolveId, if (resolveDynamicImport) { console.warn(`Cannot resolve dynamic expression in import(${spec})`); } - out += spec + after; continue; } else { // Trim any import assertions if present so that we just have the // import specifier itself. const closingQuoteIdx = spec.indexOf(quote, 1); spec = spec.slice(0, closingQuoteIdx + 1); + // @ts-ignore + item.e = code.indexOf(quote, item.s + 1); } spec = spec.replace(/^\s*(['"`])(.*)\1\s*$/g, '$2'); - if (resolveDynamicImport) { - spec = field(spec, doResolveDynamicImport, spec, id); - out += quote + spec + quote + after; - continue; - } + toResolve.push({ + spec, + // Account for opening quote + start: item.s + 1, + end: item.e, + kind: KIND_DYNAMIC_IMPORT, + property: null + }); + continue; } if (resolveId) { - spec = field(spec, resolveId, spec, id); + toResolve.push({ spec, start: item.s, end: item.e, kind: KIND_IMPORT, property: null }); } - out += quote + spec + quote + after; } - out += code.substring(offset); - - // Wait for all resolutions to finish and map them to placeholders - const mapping = new Map(); await Promise.all( - Array.from(toResolve.values()).map(async v => { - mapping.set(v.placeholder, (await v.p) || v.spec); + toResolve.map(async v => { + let resolved = null; + if (v.kind === KIND_IMPORT) { + // @ts-ignore + resolved = await resolveId(v.spec, id); + } else if (v.kind === KIND_DYNAMIC_IMPORT) { + // @ts-ignore + if (resolveDynamicImport) { + resolved = await resolveDynamicImport(v.spec, id); + } + + if (resolved == null && resolveId) { + resolved = await resolveId(v.spec, id); + } + } else if (v.kind === KIND_IMPORT_META) { + // @ts-ignore + resolved = await resolveImportMeta(v.property, id); + } + + if (!resolved) return; + + if (v.start !== v.end) { + s.overwrite(v.start, v.end, resolved); + } else { + s.prependLeft(v.start, resolved); + } }) ); - out = out.replace(/%%_RESOLVE_#\d+#_%%/g, s => mapping.get(s)); - - return out; + return { + code: s.toString(), + map: sourcemap ? s.generateMap({ source: id, file: path.posix.basename(id), includeContent: true }) : null + }; } diff --git a/packages/wmr/src/plugins/htm-plugin.js b/packages/wmr/src/plugins/htm-plugin.js index 226ca9598..e48377374 100644 --- a/packages/wmr/src/plugins/htm-plugin.js +++ b/packages/wmr/src/plugins/htm-plugin.js @@ -8,9 +8,10 @@ import transformJsxToHtmLite from '../lib/transform-jsx-to-htm-lite.js'; * @param {object} [options] * @param {RegExp | ((filename: string) => boolean)} [options.include] Controls whether files are processed to transform JSX. * @param {boolean} [options.production = true] If `false`, a simpler whitespace-preserving transform is used. + * @param {boolean} [options.sourcemap] * @returns {import('rollup').Plugin} */ -export default function htmPlugin({ include, production = true } = {}) { +export default function htmPlugin({ include, production = true, sourcemap } = {}) { return { name: 'htm-plugin', @@ -58,7 +59,9 @@ export default function htmPlugin({ include, production = true } = {}) { ] ], filename, - sourceMaps: true, + // Default is to generate sourcemaps, needs an explicit + // boolean + sourceMaps: !!sourcemap, generatorOpts: { compact: production }, diff --git a/packages/wmr/src/plugins/import-assertion.js b/packages/wmr/src/plugins/import-assertion.js index 198337063..9e9049091 100644 --- a/packages/wmr/src/plugins/import-assertion.js +++ b/packages/wmr/src/plugins/import-assertion.js @@ -13,9 +13,11 @@ import { transform } from '../lib/acorn-traverse.js'; * import foo from "json:./foo.json"; * ``` * + * @param {object} options + * @param {boolean} options.sourcemap * @returns {import("rollup").Plugin} */ -export function importAssertionPlugin() { +export function importAssertionPlugin({ sourcemap }) { return { name: 'import-assertion', options(opts) { @@ -28,8 +30,13 @@ export function importAssertionPlugin() { const res = transform(code, { parse: this.parse, - plugins: [transformImportAssertions] + plugins: [transformImportAssertions], + filename: id, + // Default is to generate sourcemaps, needs an explicit + // boolean + sourceMaps: !!sourcemap }); + return { code: res.code, map: res.map @@ -45,6 +52,32 @@ function transformImportAssertions({ types: t }) { return { name: 'transform-import-assertions', visitor: { + ImportExpression(path) { + const args = path.node.arguments; + if (!args || !args.length) return; + if (!t.isObjectExpression(args[0])) return; + + if (!args[0].properties) return; + for (let i = 0; i < args[0].properties.length; i++) { + const prop = args[0].properties[i]; + + if (!t.isIdentifier(prop.key) || prop.key.name !== 'assert') { + continue; + } + + if (!t.isObjectExpression(prop.value) || !prop.value.properties || !prop.value.properties.length) { + continue; + } + + const innerProp = prop.value.properties[0]; + if (!t.isIdentifier(innerProp.key) || innerProp.key.name !== 'type') { + continue; + } + + const type = innerProp.value.value; + path.replaceWith(t.importExpression(t.stringLiteral(`${type}:${path.node.source.value}`))); + } + }, ImportDeclaration(path) { const { assertions } = path.node; if (assertions && assertions.length > 0) { diff --git a/packages/wmr/src/plugins/process-global-plugin.js b/packages/wmr/src/plugins/process-global-plugin.js index f734ab8df..3c8c95a8b 100644 --- a/packages/wmr/src/plugins/process-global-plugin.js +++ b/packages/wmr/src/plugins/process-global-plugin.js @@ -1,4 +1,7 @@ +import MagicString from 'magic-string'; +import path from 'path'; import { transform } from '../lib/acorn-traverse.js'; +import { mergeSourceMaps } from '../lib/sourcemap.js'; /** * Plugin to replace `process.env.MY_VAR` or `import.meta.env.MY_VAR` with @@ -34,12 +37,13 @@ function acornEnvPlugin(env) { /** * Inject process globals and inline process.env.NODE_ENV. - * @param {object} [options] + * @param {object} options * @param {string} [options.NODE_ENV] constant to inline for `process.env.NODE_ENV` * @param {Record} [options.env] + * @param {boolean} [options.sourcemap] * @returns {import('rollup').Plugin} */ -export default function processGlobalPlugin({ NODE_ENV = 'development', env = {} } = {}) { +export default function processGlobalPlugin({ NODE_ENV = 'development', env = {}, sourcemap }) { const processObj = JSON.stringify({ browser: true, env: { ...env, NODE_ENV } }); const PREFIX = `\0builtins:`; @@ -54,30 +58,50 @@ export default function processGlobalPlugin({ NODE_ENV = 'development', env = {} }, transform(code, id) { if (!/\.([tj]sx?|mjs)$/.test(id)) return; - const orig = code; const result = transform(code, { plugins: [acornEnvPlugin({ ...env, NODE_ENV })], - parse: this.parse + parse: this.parse, + filename: id, + // Default is to generate sourcemaps, needs an explicit + // boolean + sourceMaps: !!sourcemap }); code = result.code; + const s = new MagicString(code); + // if that wasn't the only way `process.env` was referenced... if (code.match(/[^a-zA-Z0-9]process\.env/)) { // hack: avoid injecting imports into commonjs modules if (/^\s*(import|export)[\s{]/gm.test(code)) { + s.prepend(`import process from '${PREFIX}process.js';\n`); code = `import process from '${PREFIX}process.js';${code}`; } else { + s.prepend(`var process=${processObj};\n`); code = `var process=${processObj};${code}`; } } - code = code.replace(/typeof(\s+|\s*\(+\s*)process([^a-zA-Z$_])/g, 'typeof$1undefined$2'); - - if (code !== orig) { - return { code, map: null }; + const reg = /typeof(\s+|\s*\(+\s*)process([^a-zA-Z$_])/g; + let match = null; + while ((match = reg.exec(code)) !== null) { + s.overwrite(match.index, match[0].length, 'typeof$1undefined$2'); } + + /** @type {*} */ + const map = sourcemap + ? mergeSourceMaps([ + result.map, + s.generateMap({ source: id, file: path.posix.basename(id), includeContent: true }) + ]) + : null; + + return { + code: s.toString(), + map + }; } }; } diff --git a/packages/wmr/src/plugins/sucrase-plugin.js b/packages/wmr/src/plugins/sucrase-plugin.js index 939519b0d..5d282c253 100644 --- a/packages/wmr/src/plugins/sucrase-plugin.js +++ b/packages/wmr/src/plugins/sucrase-plugin.js @@ -56,9 +56,15 @@ export default function sucrasePlugin(opts = {}) { : undefined }); + let sourceMap = null; + if (opts.sourcemap && result.sourceMap) { + result.sourceMap.sourcesContent = [code]; + sourceMap = result.sourceMap; + } + return { code: result.code, - map: opts.sourcemap ? result.sourceMap : null + map: sourceMap }; } catch (err) { // Enhance error with code frame diff --git a/packages/wmr/src/plugins/wmr/plugin.js b/packages/wmr/src/plugins/wmr/plugin.js index b45951639..ea2a3db8d 100644 --- a/packages/wmr/src/plugins/wmr/plugin.js +++ b/packages/wmr/src/plugins/wmr/plugin.js @@ -1,5 +1,6 @@ import { promises as fs } from 'fs'; import MagicString from 'magic-string'; +import path from 'path'; import { ESM_KEYWORDS } from '../fast-cjs-plugin.js'; const BYPASS_HMR = process.env.BYPASS_HMR === 'true'; @@ -35,9 +36,12 @@ export function getWmrClient({ hot = true } = {}) { * Implements Hot Module Replacement. * Conforms to the {@link esm-hmr https://github.com/pikapkg/esm-hmr} spec. * @param {object} options + * @param {boolean} [options.hot] + * @param {boolean} [options.preact] + * @param {boolean} [options.sourcemap] * @returns {import('rollup').Plugin} */ -export default function wmrPlugin({ hot = true, preact } = {}) { +export default function wmrPlugin({ hot = true, preact, sourcemap } = {}) { if (BYPASS_HMR) hot = false; return { @@ -102,7 +106,7 @@ export default function wmrPlugin({ hot = true, preact } = {}) { return { code: s.toString(), - map: s.generateMap({ includeContent: false }) + map: sourcemap ? s.generateMap({ source: id, file: path.posix.basename(id), includeContent: true }) : null }; } }; diff --git a/packages/wmr/src/plugins/wmr/styles/styles-plugin.js b/packages/wmr/src/plugins/wmr/styles/styles-plugin.js index 378eddfda..a38f7fdf8 100644 --- a/packages/wmr/src/plugins/wmr/styles/styles-plugin.js +++ b/packages/wmr/src/plugins/wmr/styles/styles-plugin.js @@ -15,10 +15,11 @@ const RESERVED_WORDS = /^(abstract|async|boolean|break|byte|case|catch|char|clas * @param {string} options.root Manually specify the cwd from which to resolve filenames (important for calculating hashes!) * @param {boolean} [options.hot] Indicates the plugin should inject a HMR-runtime * @param {boolean} [options.production] + * @param {boolean} [options.sourcemap] * @param {Record} options.alias * @returns {import('rollup').Plugin} */ -export default function wmrStylesPlugin({ root, hot, production, alias }) { +export default function wmrStylesPlugin({ root, hot, production, alias, sourcemap }) { let assetId = 0; const assetMap = new Map(); /** @type {Map>} */ @@ -149,7 +150,14 @@ export default function wmrStylesPlugin({ root, hot, production, alias }) { code, moduleSideEffects: true, syntheticNamedExports: true, - map: null + map: sourcemap + ? { + version: 3, + sources: [], + mappings: '', + names: [] + } + : null }; }, async generateBundle(_, bundle) { diff --git a/packages/wmr/src/server.js b/packages/wmr/src/server.js index b111528c8..82ccc9733 100644 --- a/packages/wmr/src/server.js +++ b/packages/wmr/src/server.js @@ -106,6 +106,20 @@ export default async function server({ cwd, root, overlayDir, middleware, http2, app.use('/@npm', npmMiddleware({ alias, optimize, cwd })); + // Chrome devtools often adds `?%20[sm]` to the url + // to differentiate between sourcemaps + app.use((req, res, next) => { + if (/\?%20\[sm\]$/.test(req.url)) { + res.writeHead(302, { + Location: req.url.replace('?%20[sm]', '.map') + }); + res.end(); + return; + } + + next(); + }); + if (middleware) { app.use(...middleware); } diff --git a/packages/wmr/src/wmr-middleware.js b/packages/wmr/src/wmr-middleware.js index 1ff637a2a..bab2db3ff 100644 --- a/packages/wmr/src/wmr-middleware.js +++ b/packages/wmr/src/wmr-middleware.js @@ -11,6 +11,7 @@ import { getPlugins } from './lib/plugins.js'; import { watch } from './lib/fs-watcher.js'; import { matchAlias, resolveAlias } from './lib/aliasing.js'; import { addTimestamp } from './lib/net-utils.js'; +import { mergeSourceMaps } from './lib/sourcemap.js'; const NOOP = () => {}; @@ -32,12 +33,13 @@ export const moduleGraph = new Map(); * @returns {import('polka').Middleware} */ export default function wmrMiddleware(options) { - let { cwd, root, out, distDir = 'dist', onError, onChange = NOOP, alias } = options; + let { cwd, root, out, distDir = 'dist', onError, onChange = NOOP, alias, sourcemap } = options; distDir = resolve(dirname(out), distDir); const NonRollup = createPluginContainer(getPlugins(options), { cwd: root, + sourcemap, writeFile: (filename, source) => { // Remove .cache folder from filename if present. The cache // works with relative keys only. @@ -155,6 +157,16 @@ export default function wmrMiddleware(options) { logCache(`delete: ${kl.cyan(file)}`); WRITE_CACHE.delete(file); + // Delete source file if there is any + if (options.sourcemap) { + const sourceKey = file + '.map'; + + if (WRITE_CACHE.has(sourceKey)) { + logCache(`delete: ${kl.cyan(sourceKey)}`); + WRITE_CACHE.delete(sourceKey); + } + } + // We could be dealing with an asset if (WRITE_CACHE.has(file + '?asset')) { const cacheKey = file + '?asset'; @@ -281,6 +293,8 @@ export default function wmrMiddleware(options) { let transform; if (path === '/_wmr.js') { transform = getWmrClient.bind(null); + } else if (/\.map$/.test(path)) { + transform = TRANSFORMS.asset; } else if (queryParams.has('asset')) { cacheKey += '?asset'; transform = TRANSFORMS.asset; @@ -473,11 +487,16 @@ export const TRANSFORMS = { file = file.split(posix.sep).join(sep); if (!isAbsolute(file)) file = resolve(root, file); code = await fs.readFile(resolveFile(file, root, alias), 'utf-8'); + + // TODO: Optional: Load sourcemap } - code = await NonRollup.transform(code, id); + const transformed = await NonRollup.transform(code, id); + code = transformed.code; + /** @type {import('rollup').ExistingRawSourceMap | null} */ + let sourceMap = transformed.map; - code = await transformImports(code, id, { + const rewritten = await transformImports(code, id, { resolveImportMeta(property) { return NonRollup.resolveImportMeta(property); }, @@ -598,6 +617,20 @@ export const TRANSFORMS = { } }); + if (rewritten.map !== null) { + if (sourceMap !== null) { + // @ts-ignore + sourceMap = mergeSourceMaps([sourceMap, rewritten.map]); + } else { + sourceMap = rewritten.map; + } + } + code = rewritten.code; + + if (sourceMap !== null) { + writeCacheFile(cacheKey + '.map', JSON.stringify(sourceMap)); + code = `${code}\n//# sourceMappingURL=${basename(id)}.map`; + } writeCacheFile(cacheKey, code); return code; diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/dynamic-import-inline.expected.js b/packages/wmr/test/fixtures/_unit-transform-imports/dynamic-import-inline.expected.js new file mode 100644 index 000000000..4d9bb656b --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/dynamic-import-inline.expected.js @@ -0,0 +1,2 @@ +// @ts-ignore +const About = lazy(() => import('it_works')); diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/dynamic-import-inline.js b/packages/wmr/test/fixtures/_unit-transform-imports/dynamic-import-inline.js new file mode 100644 index 000000000..b74f3689a --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/dynamic-import-inline.js @@ -0,0 +1,2 @@ +// @ts-ignore +const About = lazy(() => import('./pages/about/index.js')); diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/dynamic-import.expected.js b/packages/wmr/test/fixtures/_unit-transform-imports/dynamic-import.expected.js new file mode 100644 index 000000000..c8aee9c09 --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/dynamic-import.expected.js @@ -0,0 +1,2 @@ +// @ts-ignore +import('it_works'); diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/dynamic-import.js b/packages/wmr/test/fixtures/_unit-transform-imports/dynamic-import.js new file mode 100644 index 000000000..131dc8297 --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/dynamic-import.js @@ -0,0 +1,2 @@ +// @ts-ignore +import('foo'); diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/import-assertion-dynamic.expected.js b/packages/wmr/test/fixtures/_unit-transform-imports/import-assertion-dynamic.expected.js new file mode 100644 index 000000000..b2b855c00 --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/import-assertion-dynamic.expected.js @@ -0,0 +1,2 @@ +// @ts-ignore +import('it_works_dynamic', { assert: { type: 'json' } }); diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/import-assertion-dynamic.js b/packages/wmr/test/fixtures/_unit-transform-imports/import-assertion-dynamic.js new file mode 100644 index 000000000..53ea85211 --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/import-assertion-dynamic.js @@ -0,0 +1,2 @@ +// @ts-ignore +import('foo', { assert: { type: 'json' } }); diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/import-assertion.expected.js b/packages/wmr/test/fixtures/_unit-transform-imports/import-assertion.expected.js new file mode 100644 index 000000000..6411ad23a --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/import-assertion.expected.js @@ -0,0 +1 @@ +import json from 'it_works' assert { type: 'json' }; diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/import-assertion.js b/packages/wmr/test/fixtures/_unit-transform-imports/import-assertion.js new file mode 100644 index 000000000..39bc625b3 --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/import-assertion.js @@ -0,0 +1 @@ +import json from './foo.json' assert { type: 'json' }; diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/import-meta-inline.expected.js b/packages/wmr/test/fixtures/_unit-transform-imports/import-meta-inline.expected.js new file mode 100644 index 000000000..33df6da16 --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/import-meta-inline.expected.js @@ -0,0 +1,7 @@ +// @ts-ignore +let m = import(foo); +// @ts-ignore +bar.accept(async ({ module }) => { + // @ts-ignore + m = await m; +}); diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/import-meta-inline.js b/packages/wmr/test/fixtures/_unit-transform-imports/import-meta-inline.js new file mode 100644 index 000000000..041bb7d9a --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/import-meta-inline.js @@ -0,0 +1,7 @@ +// @ts-ignore +let m = import(import.meta.url); +// @ts-ignore +import.meta.hot.accept(async ({ module }) => { + // @ts-ignore + m = await m; +}); diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/import-meta-long.expected.js b/packages/wmr/test/fixtures/_unit-transform-imports/import-meta-long.expected.js new file mode 100644 index 000000000..daef2a221 --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/import-meta-long.expected.js @@ -0,0 +1,2 @@ +// @ts-ignore +import(it_works.NODE_ENV); diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/import-meta-long.js b/packages/wmr/test/fixtures/_unit-transform-imports/import-meta-long.js new file mode 100644 index 000000000..d9f11432d --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/import-meta-long.js @@ -0,0 +1,2 @@ +// @ts-ignore +import(import.meta.env.NODE_ENV); diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/import-meta.expected.js b/packages/wmr/test/fixtures/_unit-transform-imports/import-meta.expected.js new file mode 100644 index 000000000..b2a661669 --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/import-meta.expected.js @@ -0,0 +1,2 @@ +// @ts-ignore +import(it_works); diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/import-meta.js b/packages/wmr/test/fixtures/_unit-transform-imports/import-meta.js new file mode 100644 index 000000000..749f930c9 --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/import-meta.js @@ -0,0 +1,2 @@ +// @ts-ignore +import(import.meta.foo); diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/static-import-empty.expected.js b/packages/wmr/test/fixtures/_unit-transform-imports/static-import-empty.expected.js new file mode 100644 index 000000000..22c3d2111 --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/static-import-empty.expected.js @@ -0,0 +1,5 @@ +// @ts-ignore +import { foo } from 'foo'; +// @ts-ignore +import bar from 'foo'; +import 'foo'; diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/static-import-empty.js b/packages/wmr/test/fixtures/_unit-transform-imports/static-import-empty.js new file mode 100644 index 000000000..a3c778afe --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/static-import-empty.js @@ -0,0 +1,5 @@ +// @ts-ignore +import { foo } from ''; +// @ts-ignore +import bar from ''; +import ''; diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/static-import-relative.expected.js b/packages/wmr/test/fixtures/_unit-transform-imports/static-import-relative.expected.js new file mode 100644 index 000000000..4f3966abc --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/static-import-relative.expected.js @@ -0,0 +1,2 @@ +// @ts-ignore +import { foo } from 'it_works'; diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/static-import-relative.js b/packages/wmr/test/fixtures/_unit-transform-imports/static-import-relative.js new file mode 100644 index 000000000..611acc0a8 --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/static-import-relative.js @@ -0,0 +1,2 @@ +// @ts-ignore +import { foo } from './foo.js?module'; diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/static-import.expected.js b/packages/wmr/test/fixtures/_unit-transform-imports/static-import.expected.js new file mode 100644 index 000000000..b40f293fe --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/static-import.expected.js @@ -0,0 +1,5 @@ +// @ts-ignore +import { foo } from 'a'; +// @ts-ignore +import bar from 'b'; +import 'c'; diff --git a/packages/wmr/test/fixtures/_unit-transform-imports/static-import.js b/packages/wmr/test/fixtures/_unit-transform-imports/static-import.js new file mode 100644 index 000000000..f94f620be --- /dev/null +++ b/packages/wmr/test/fixtures/_unit-transform-imports/static-import.js @@ -0,0 +1,5 @@ +// @ts-ignore +import { foo } from 'foo'; +// @ts-ignore +import bar from 'bar'; +import 'baz'; diff --git a/packages/wmr/test/fixtures/sourcemap-jsx/index.html b/packages/wmr/test/fixtures/sourcemap-jsx/index.html new file mode 100644 index 000000000..bd2a2bce2 --- /dev/null +++ b/packages/wmr/test/fixtures/sourcemap-jsx/index.html @@ -0,0 +1,2 @@ +
it doesn't work
+ diff --git a/packages/wmr/test/fixtures/sourcemap-jsx/index.jsx b/packages/wmr/test/fixtures/sourcemap-jsx/index.jsx new file mode 100644 index 000000000..623537a74 --- /dev/null +++ b/packages/wmr/test/fixtures/sourcemap-jsx/index.jsx @@ -0,0 +1,8 @@ +import { render } from 'preact'; + +function App() { + return

it works

; +} + +document.getElementById('out').textContent = ''; +render(, document.getElementById('out')); diff --git a/packages/wmr/test/fixtures/sourcemap-ts/index.html b/packages/wmr/test/fixtures/sourcemap-ts/index.html new file mode 100644 index 000000000..950511873 --- /dev/null +++ b/packages/wmr/test/fixtures/sourcemap-ts/index.html @@ -0,0 +1,2 @@ +
it doesn't work
+ diff --git a/packages/wmr/test/fixtures/sourcemap-ts/index.ts b/packages/wmr/test/fixtures/sourcemap-ts/index.ts new file mode 100644 index 000000000..b77d1d853 --- /dev/null +++ b/packages/wmr/test/fixtures/sourcemap-ts/index.ts @@ -0,0 +1,9 @@ +export interface Foo { + foo: string; +} + +function getFoo(foo: Foo) { + return foo.foo; +} + +document.getElementById('out').textContent = getFoo({ foo: 'it works' }); diff --git a/packages/wmr/test/sourcemaps.test.js b/packages/wmr/test/sourcemaps.test.js new file mode 100644 index 000000000..f770217c5 --- /dev/null +++ b/packages/wmr/test/sourcemaps.test.js @@ -0,0 +1,72 @@ +import { setupTest, teardown, loadFixture, runWmrFast, getOutput, withLog } from './test-helpers.js'; + +jest.setTimeout(30000); + +describe('sourcemaps', () => { + /** @type {TestEnv} */ + let env; + /** @type {WmrInstance} */ + let instance; + + beforeEach(async () => { + env = await setupTest(); + }); + + afterEach(async () => { + await teardown(env); + instance.close(); + }); + + it('should import absolute file', async () => { + await loadFixture('sourcemap-ts', env); + instance = await runWmrFast(env.tmp.path, '--sourcemap'); + + await withLog(instance.output, async () => { + expect(await getOutput(env, instance)).toMatch(/it works/); + expect(await env.page.evaluate('fetch("/index.ts.map").then(r => r.json())')).toEqual({ + file: 'index.ts', + mappings: 'AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;', + names: [], + sources: ['/index.ts'], + sourcesContent: [ + `export interface Foo { + foo: string; +} + +function getFoo(foo: Foo) { + return foo.foo; +} + +document.getElementById('out').textContent = getFoo({ foo: 'it works' });\n` + ], + version: 3 + }); + }); + }); + + it('should map jsx', async () => { + await loadFixture('sourcemap-jsx', env); + instance = await runWmrFast(env.tmp.path, '--sourcemap'); + + await withLog(instance.output, async () => { + expect(await getOutput(env, instance)).toMatch(/it works/); + expect(await env.page.evaluate('fetch("/index.jsx.map").then(r => r.json())')).toEqual({ + file: 'index.jsx', + mappings: 'AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;', + names: [], + sources: ['/index.jsx'], + sourcesContent: [ + `import { render } from 'preact'; + +function App() { + return

it works

; +} + +document.getElementById('out').textContent = ''; +render(, document.getElementById('out'));\n` + ], + version: 3 + }); + }); + }); +}); diff --git a/packages/wmr/test/transform-imports.test.js b/packages/wmr/test/transform-imports.test.js new file mode 100644 index 000000000..905070a85 --- /dev/null +++ b/packages/wmr/test/transform-imports.test.js @@ -0,0 +1,182 @@ +import path from 'path'; +import { promises as fs } from 'fs'; +import { transformImports } from '../src/lib/transform-imports.js'; + +/** + * @param {string} file + * @returns {Promise} + */ +async function readFile(file) { + return fs.readFile(path.join(__dirname, 'fixtures', '_unit-transform-imports', file), 'utf-8'); +} + +describe('transformImports', () => { + it('should generate source maps', async () => { + const code = await readFile('static-import.js'); + const result = await transformImports(code, 'my-test', { + sourcemap: true, + resolveId(id) { + if (id === 'foo') return 'a'; + if (id === 'bar') return 'b'; + if (id === 'baz') return 'c'; + return 'nope'; + } + }); + expect(result.map).not.toEqual(null); + }); + + describe('import statement', () => { + it('should transform imports', async () => { + const code = await readFile('static-import.js'); + const expected = await readFile('static-import.expected.js'); + const result = await transformImports(code, 'my-test', { + resolveId(id) { + if (id === 'foo') return 'a'; + if (id === 'bar') return 'b'; + if (id === 'baz') return 'c'; + return 'nope'; + } + }); + expect(result.code).toEqual(expected); + }); + + it('should transform empty imports', async () => { + const code = await readFile('static-import-empty.js'); + const expected = await readFile('static-import-empty.expected.js'); + const result = await transformImports(code, 'my-test', { + resolveId() { + return 'foo'; + } + }); + expect(result.code).toEqual(expected); + }); + + it('should preserve relative spec', async () => { + const code = await readFile('static-import-relative.js'); + const expected = await readFile('static-import-relative.expected.js'); + + const result = await transformImports(code, 'my-test', { + resolveId(id) { + if (id === './foo.js?module') { + return 'it_works'; + } + } + }); + expect(result.code).toEqual(expected); + }); + }); + + describe('dynamic import', () => { + it('should transform imports', async () => { + const code = await readFile('dynamic-import.js'); + const expected = await readFile('dynamic-import.expected.js'); + const result = await transformImports(code, 'my-test', { + resolveDynamicImport(id) { + if (id === 'foo') { + return 'it_works'; + } + } + }); + expect(result.code).toEqual(expected); + }); + + it('should transform inline', async () => { + const code = await readFile('dynamic-import-inline.js'); + const expected = await readFile('dynamic-import-inline.expected.js'); + const result = await transformImports(code, 'my-test', { + resolveDynamicImport(id) { + if (id === './pages/about/index.js') { + return 'it_works'; + } + } + }); + expect(result.code).toEqual(expected); + }); + }); + + describe('import.meta', () => { + it('should transform simple property', async () => { + const code = await readFile('import-meta.js'); + const expected = await readFile('import-meta.expected.js'); + const result = await transformImports(code, 'my-test', { + resolveImportMeta(property) { + if (property === 'foo') { + return 'it_works'; + } + } + }); + expect(result.code).toEqual(expected); + }); + + it('should transform nested property', async () => { + const code = await readFile('import-meta-long.js'); + const expected = await readFile('import-meta-long.expected.js'); + const result = await transformImports(code, 'my-test', { + resolveImportMeta(property) { + if (property === 'env') { + return 'it_works'; + } + } + }); + expect(result.code).toEqual(expected); + }); + + it('should transform inline', async () => { + const code = await readFile('import-meta-inline.js'); + const expected = await readFile('import-meta-inline.expected.js'); + const result = await transformImports(code, 'my-test', { + resolveId(id) { + throw new Error(`Called resolveId(${id})`); + }, + resolveDynamicImport(id) { + throw new Error(`Called resolveDynamicImport(${id})`); + }, + resolveImportMeta(property) { + if (property === 'url') { + return 'foo'; + } else if (property === 'hot') { + return 'bar'; + } + return 'nope'; + } + }); + expect(result.code).toEqual(expected); + }); + }); + + describe('import assertions', () => { + it('should ignore in static imports', async () => { + const code = await readFile('import-assertion.js'); + const expected = await readFile('import-assertion.expected.js'); + const result = await transformImports(code, 'my-test', { + resolveId() { + return 'it_works'; + }, + resolveDynamicImport() { + return 'it_works_dynamic'; + }, + resolveImportMeta() { + return 'it_works_meta'; + } + }); + expect(result.code).toEqual(expected); + }); + + it('should ignore in dynamic imports', async () => { + const code = await readFile('import-assertion-dynamic.js'); + const expected = await readFile('import-assertion-dynamic.expected.js'); + const result = await transformImports(code, 'my-test', { + resolveId() { + return 'it_works'; + }, + resolveDynamicImport() { + return 'it_works_dynamic'; + }, + resolveImportMeta() { + return 'it_works_meta'; + } + }); + expect(result.code).toEqual(expected); + }); + }); +}); diff --git a/patches/es-module-lexer+0.4.1.patch b/patches/es-module-lexer+0.4.1.patch new file mode 100644 index 000000000..d5103875e --- /dev/null +++ b/patches/es-module-lexer+0.4.1.patch @@ -0,0 +1,19 @@ +diff --git a/node_modules/es-module-lexer/package.json b/node_modules/es-module-lexer/package.json +index 0c49d6e..4b9d388 100755 +--- a/node_modules/es-module-lexer/package.json ++++ b/node_modules/es-module-lexer/package.json +@@ -5,6 +5,14 @@ + "main": "dist/lexer.cjs", + "module": "dist/lexer.js", + "types": "types/lexer.d.ts", ++ "exports": { ++ ".": { ++ "import": "./dist/lexer.js", ++ "require": "./dist/lexer.cjs" ++ }, ++ "./package.json": "./package.json", ++ "./": "./" ++ }, + "scripts": { + "test": "NODE_OPTIONS=\"--experimental-modules\" mocha -b -u tdd test/*.cjs", + "build": "node --experimental-modules build.js && babel dist/lexer.js | terser -o dist/lexer.cjs", diff --git a/patches/sourcemap-codec+1.4.8.patch b/patches/sourcemap-codec+1.4.8.patch new file mode 100644 index 000000000..5c5117ea6 --- /dev/null +++ b/patches/sourcemap-codec+1.4.8.patch @@ -0,0 +1,162 @@ +diff --git a/node_modules/sourcemap-codec/.DS_Store b/node_modules/sourcemap-codec/.DS_Store +new file mode 100644 +index 0000000..f7b71aa +Binary files /dev/null and b/node_modules/sourcemap-codec/.DS_Store differ +diff --git a/node_modules/sourcemap-codec/dist/sourcemap-codec.mjs b/node_modules/sourcemap-codec/dist/sourcemap-codec.mjs +new file mode 100644 +index 0000000..4e3813e +--- /dev/null ++++ b/node_modules/sourcemap-codec/dist/sourcemap-codec.mjs +@@ -0,0 +1,124 @@ ++var charToInteger = {}; ++var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; ++for (var i = 0; i < chars.length; i++) { ++ charToInteger[chars.charCodeAt(i)] = i; ++} ++function decode(mappings) { ++ var decoded = []; ++ var line = []; ++ var segment = [ ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ ]; ++ var j = 0; ++ for (var i = 0, shift = 0, value = 0; i < mappings.length; i++) { ++ var c = mappings.charCodeAt(i); ++ if (c === 44) { // "," ++ segmentify(line, segment, j); ++ j = 0; ++ } ++ else if (c === 59) { // ";" ++ segmentify(line, segment, j); ++ j = 0; ++ decoded.push(line); ++ line = []; ++ segment[0] = 0; ++ } ++ else { ++ var integer = charToInteger[c]; ++ if (integer === undefined) { ++ throw new Error('Invalid character (' + String.fromCharCode(c) + ')'); ++ } ++ var hasContinuationBit = integer & 32; ++ integer &= 31; ++ value += integer << shift; ++ if (hasContinuationBit) { ++ shift += 5; ++ } ++ else { ++ var shouldNegate = value & 1; ++ value >>>= 1; ++ if (shouldNegate) { ++ value = value === 0 ? -0x80000000 : -value; ++ } ++ segment[j] += value; ++ j++; ++ value = shift = 0; // reset ++ } ++ } ++ } ++ segmentify(line, segment, j); ++ decoded.push(line); ++ return decoded; ++} ++function segmentify(line, segment, j) { ++ // This looks ugly, but we're creating specialized arrays with a specific ++ // length. This is much faster than creating a new array (which v8 expands to ++ // a capacity of 17 after pushing the first item), or slicing out a subarray ++ // (which is slow). Length 4 is assumed to be the most frequent, followed by ++ // length 5 (since not everything will have an associated name), followed by ++ // length 1 (it's probably rare for a source substring to not have an ++ // associated segment data). ++ if (j === 4) ++ line.push([segment[0], segment[1], segment[2], segment[3]]); ++ else if (j === 5) ++ line.push([segment[0], segment[1], segment[2], segment[3], segment[4]]); ++ else if (j === 1) ++ line.push([segment[0]]); ++} ++function encode(decoded) { ++ var sourceFileIndex = 0; // second field ++ var sourceCodeLine = 0; // third field ++ var sourceCodeColumn = 0; // fourth field ++ var nameIndex = 0; // fifth field ++ var mappings = ''; ++ for (var i = 0; i < decoded.length; i++) { ++ var line = decoded[i]; ++ if (i > 0) ++ mappings += ';'; ++ if (line.length === 0) ++ continue; ++ var generatedCodeColumn = 0; // first field ++ var lineMappings = []; ++ for (var _i = 0, line_1 = line; _i < line_1.length; _i++) { ++ var segment = line_1[_i]; ++ var segmentMappings = encodeInteger(segment[0] - generatedCodeColumn); ++ generatedCodeColumn = segment[0]; ++ if (segment.length > 1) { ++ segmentMappings += ++ encodeInteger(segment[1] - sourceFileIndex) + ++ encodeInteger(segment[2] - sourceCodeLine) + ++ encodeInteger(segment[3] - sourceCodeColumn); ++ sourceFileIndex = segment[1]; ++ sourceCodeLine = segment[2]; ++ sourceCodeColumn = segment[3]; ++ } ++ if (segment.length === 5) { ++ segmentMappings += encodeInteger(segment[4] - nameIndex); ++ nameIndex = segment[4]; ++ } ++ lineMappings.push(segmentMappings); ++ } ++ mappings += lineMappings.join(','); ++ } ++ return mappings; ++} ++function encodeInteger(num) { ++ var result = ''; ++ num = num < 0 ? (-num << 1) | 1 : num << 1; ++ do { ++ var clamped = num & 31; ++ num >>>= 5; ++ if (num > 0) { ++ clamped |= 32; ++ } ++ result += chars[clamped]; ++ } while (num > 0); ++ return result; ++} ++ ++export { decode, encode }; ++//# sourceMappingURL=sourcemap-codec.mjs.map +diff --git a/node_modules/sourcemap-codec/dist/sourcemap-codec.mjs.map b/node_modules/sourcemap-codec/dist/sourcemap-codec.mjs.map +new file mode 100644 +index 0000000..5b2c6ed +--- /dev/null ++++ b/node_modules/sourcemap-codec/dist/sourcemap-codec.mjs.map +@@ -0,0 +1 @@ ++{"version":3,"file":"sourcemap-codec.mjs","sources":["../src/sourcemap-codec.ts"],"sourcesContent":["export type SourceMapSegment =\n\t| [number]\n\t| [number, number, number, number]\n\t| [number, number, number, number, number];\nexport type SourceMapLine = SourceMapSegment[];\nexport type SourceMapMappings = SourceMapLine[];\n\nconst charToInteger: { [charCode: number]: number } = {};\nconst chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';\n\nfor (let i = 0; i < chars.length; i++) {\n\tcharToInteger[chars.charCodeAt(i)] = i;\n}\n\nexport function decode(mappings: string): SourceMapMappings {\n\tconst decoded: SourceMapMappings = [];\n\tlet line: SourceMapLine = [];\n\tconst segment: SourceMapSegment = [\n\t\t0, // generated code column\n\t\t0, // source file index\n\t\t0, // source code line\n\t\t0, // source code column\n\t\t0, // name index\n\t];\n\n\tlet j = 0;\n\tfor (let i = 0, shift = 0, value = 0; i < mappings.length; i++) {\n\t\tconst c = mappings.charCodeAt(i);\n\n\t\tif (c === 44) { // \",\"\n\t\t\tsegmentify(line, segment, j);\n\t\t\tj = 0;\n\n\t\t} else if (c === 59) { // \";\"\n\t\t\tsegmentify(line, segment, j);\n\t\t\tj = 0;\n\t\t\tdecoded.push(line);\n\t\t\tline = [];\n\t\t\tsegment[0] = 0;\n\n\t\t} else {\n\t\t\tlet integer = charToInteger[c];\n\t\t\tif (integer === undefined) {\n\t\t\t\tthrow new Error('Invalid character (' + String.fromCharCode(c) + ')');\n\t\t\t}\n\n\t\t\tconst hasContinuationBit = integer & 32;\n\n\t\t\tinteger &= 31;\n\t\t\tvalue += integer << shift;\n\n\t\t\tif (hasContinuationBit) {\n\t\t\t\tshift += 5;\n\t\t\t} else {\n\t\t\t\tconst shouldNegate = value & 1;\n\t\t\t\tvalue >>>= 1;\n\n\t\t\t\tif (shouldNegate) {\n\t\t\t\t\tvalue = value === 0 ? -0x80000000 : -value;\n\t\t\t\t}\n\n\t\t\t\tsegment[j] += value;\n\t\t\t\tj++;\n\t\t\t\tvalue = shift = 0; // reset\n\t\t\t}\n\t\t}\n\t}\n\n\tsegmentify(line, segment, j);\n\tdecoded.push(line);\n\n\treturn decoded;\n}\n\nfunction segmentify(line: SourceMapSegment[], segment: SourceMapSegment, j: number) {\n\t// This looks ugly, but we're creating specialized arrays with a specific\n\t// length. This is much faster than creating a new array (which v8 expands to\n\t// a capacity of 17 after pushing the first item), or slicing out a subarray\n\t// (which is slow). Length 4 is assumed to be the most frequent, followed by\n\t// length 5 (since not everything will have an associated name), followed by\n\t// length 1 (it's probably rare for a source substring to not have an\n\t// associated segment data).\n\tif (j === 4) line.push([segment[0], segment[1], segment[2], segment[3]]);\n\telse if (j === 5) line.push([segment[0], segment[1], segment[2], segment[3], segment[4]]);\n\telse if (j === 1) line.push([segment[0]]);\n}\n\nexport function encode(decoded: SourceMapMappings): string {\n\tlet sourceFileIndex = 0; // second field\n\tlet sourceCodeLine = 0; // third field\n\tlet sourceCodeColumn = 0; // fourth field\n\tlet nameIndex = 0; // fifth field\n\tlet mappings = '';\n\n\tfor (let i = 0; i < decoded.length; i++) {\n\t\tconst line = decoded[i];\n\t\tif (i > 0) mappings += ';';\n\t\tif (line.length === 0) continue;\n\n\t\tlet generatedCodeColumn = 0; // first field\n\n\t\tconst lineMappings: string[] = [];\n\n\t\tfor (const segment of line) {\n\t\t\tlet segmentMappings = encodeInteger(segment[0] - generatedCodeColumn);\n\t\t\tgeneratedCodeColumn = segment[0];\n\n\t\t\tif (segment.length > 1) {\n\t\t\t\tsegmentMappings +=\n\t\t\t\t\tencodeInteger(segment[1] - sourceFileIndex) +\n\t\t\t\t\tencodeInteger(segment[2] - sourceCodeLine) +\n\t\t\t\t\tencodeInteger(segment[3] - sourceCodeColumn);\n\n\t\t\t\tsourceFileIndex = segment[1];\n\t\t\t\tsourceCodeLine = segment[2];\n\t\t\t\tsourceCodeColumn = segment[3];\n\t\t\t}\n\n\t\t\tif (segment.length === 5) {\n\t\t\t\tsegmentMappings += encodeInteger(segment[4] - nameIndex);\n\t\t\t\tnameIndex = segment[4];\n\t\t\t}\n\n\t\t\tlineMappings.push(segmentMappings);\n\t\t}\n\n\t\tmappings += lineMappings.join(',');\n\t}\n\n\treturn mappings;\n}\n\nfunction encodeInteger(num: number): string {\n\tvar result = '';\n\tnum = num < 0 ? (-num << 1) | 1 : num << 1;\n\tdo {\n\t\tvar clamped = num & 31;\n\t\tnum >>>= 5;\n\t\tif (num > 0) {\n\t\t\tclamped |= 32;\n\t\t}\n\t\tresult += chars[clamped];\n\t} while (num > 0);\n\n\treturn result;\n}\n"],"names":[],"mappings":"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"} +\ No newline at end of file +diff --git a/node_modules/sourcemap-codec/package.json b/node_modules/sourcemap-codec/package.json +index 4b2d219..96f5feb 100644 +--- a/node_modules/sourcemap-codec/package.json ++++ b/node_modules/sourcemap-codec/package.json +@@ -5,6 +5,15 @@ + "main": "dist/sourcemap-codec.umd.js", + "module": "dist/sourcemap-codec.es.js", + "types": "dist/types/sourcemap-codec.d.ts", ++ "exports": { ++ ".": { ++ "browser": "./dist/sourcemap-codec.umd.js", ++ "import": "./dist/sourcemap-codec.mjs", ++ "require": "./dist/sourcemap-codec.umd.js" ++ }, ++ "./package.json": "./package.json", ++ "./": "./" ++ }, + "scripts": { + "test": "mocha", + "build": "rm -rf dist && rollup -c && tsc", diff --git a/yarn.lock b/yarn.lock index b556decc2..1138e33b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1286,6 +1286,11 @@ semver "^7.3.2" tsutils "^3.17.1" +"@yarnpkg/lockfile@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + JSV@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/JSV/-/JSV-4.0.2.tgz#d077f6825571f82132f9dffaed587b4029feff57" @@ -3914,6 +3919,13 @@ find-yarn-workspace-root2@1.2.16: micromatch "^4.0.2" pkg-dir "^4.2.0" +find-yarn-workspace-root@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" + integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== + dependencies: + micromatch "^4.0.2" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -5748,6 +5760,13 @@ kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c" + integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== + dependencies: + graceful-fs "^4.1.11" + kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -6938,6 +6957,25 @@ password-prompt@^1.0.4: ansi-escapes "^3.1.0" cross-spawn "^6.0.5" +patch-package@^6.4.7: + version "6.4.7" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.4.7.tgz#2282d53c397909a0d9ef92dae3fdeb558382b148" + integrity sha512-S0vh/ZEafZ17hbhgqdnpunKDfzHQibQizx9g8yEf5dcVk3KOflOfdufRXQX8CSEkyOQwuM/bNz1GwKvFj54kaQ== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^2.4.2" + cross-spawn "^6.0.5" + find-yarn-workspace-root "^2.0.0" + fs-extra "^7.0.1" + is-ci "^2.0.0" + klaw-sync "^6.0.0" + minimist "^1.2.0" + open "^7.4.2" + rimraf "^2.6.3" + semver "^5.6.0" + slash "^2.0.0" + tmp "^0.0.33" + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -8306,6 +8344,11 @@ sisteransi@^1.0.5: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -8409,7 +8452,7 @@ source-map@^0.7.3, source-map@~0.7.2: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== -sourcemap-codec@^1.4.4: +sourcemap-codec@^1.4.4, sourcemap-codec@^1.4.8: version "1.4.8" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==