From 8ee6bf16f985b8dfc75619e8de10ec854d242bf4 Mon Sep 17 00:00:00 2001 From: Jason Tucker Date: Mon, 26 Aug 2024 18:33:38 -0400 Subject: [PATCH] feat(core): Add inline source-maps This provides error information in the browser. Including the original source Fixes: #198 --- build/rollup.config-node.mjs | 2 +- build/webpack.config.js | 2 +- src/createVue2SFCModule.ts | 4 ++-- src/createVue3SFCModule.ts | 25 ++++++++++++++++++------- src/tools.ts | 35 +++++++++++++++++++++++++++++------ src/types.ts | 2 +- 6 files changed, 52 insertions(+), 18 deletions(-) diff --git a/build/rollup.config-node.mjs b/build/rollup.config-node.mjs index 410acfb..2c4c905 100644 --- a/build/rollup.config-node.mjs +++ b/build/rollup.config-node.mjs @@ -10,7 +10,7 @@ const __dirname = fileURLToPath(new URL('.', import.meta.url)) import pkg from '../package.json' assert { type: 'json' }; -const genSourcemap = false; +const genSourcemap = "inline"; async function configFactory({ name, vueTarget, libraryTargetModule }) { diff --git a/build/webpack.config.js b/build/webpack.config.js index 90c8d34..89c6552 100644 --- a/build/webpack.config.js +++ b/build/webpack.config.js @@ -88,7 +88,7 @@ const configure = ({name, vueTarget, libraryTargetModule}) => async (env = {}, { } })); - const genSourcemap = false; + const genSourcemap = "inline"; let actualTargetsBrowsers = targetsBrowsers; diff --git a/src/createVue2SFCModule.ts b/src/createVue2SFCModule.ts index 84afac7..bcf855e 100644 --- a/src/createVue2SFCModule.ts +++ b/src/createVue2SFCModule.ts @@ -50,7 +50,7 @@ import { version, vueVersion } from './index' // @ts-ignore const targetBrowserBabelPluginsHash : string = hash(...Object.keys({ ...(typeof ___targetBrowserBabelPlugins !== 'undefined' ? ___targetBrowserBabelPlugins : {}) })); -const genSourcemap : boolean = !!process.env.GEN_SOURCEMAP; +const genSourcemap : boolean | "inline" = process.env.GEN_SOURCEMAP; /** * @internal @@ -88,7 +88,7 @@ export async function createSFCModule(source : string, filename : AbstractPath, const descriptor = sfc_parse({ source, filename: strFilename, - needMap: genSourcemap, + needMap: !!genSourcemap, compiler: vueTemplateCompiler as VueTemplateCompiler} ); diff --git a/src/createVue3SFCModule.ts b/src/createVue3SFCModule.ts index f7bfeaf..dd15c34 100644 --- a/src/createVue3SFCModule.ts +++ b/src/createVue3SFCModule.ts @@ -44,18 +44,22 @@ type PreprocessLang = SFCAsyncStyleCompileOptions['preprocessLang']; * the version of the library */ import { version, vueVersion } from './index' +import { RawSourceMap } from '@vue/component-compiler-utils/dist/types' // @ts-ignore const targetBrowserBabelPluginsHash : string = hash(...Object.keys({ ...(typeof ___targetBrowserBabelPlugins !== 'undefined' ? ___targetBrowserBabelPlugins : {}) })); -const genSourcemap : boolean = !!process.env.GEN_SOURCEMAP; +const genSourcemap : boolean | "inline" = process.env.GEN_SOURCEMAP; /** * @internal */ const isProd : boolean = process.env.NODE_ENV === 'production'; - +/** + * @internal + */ +const buildMapComment = (map : RawSourceMap) => `//# sourceMappingURL=data:application/json;charset=utf-8;base64,${btoa(JSON.stringify(map))}` /** * @internal @@ -87,7 +91,7 @@ export async function createSFCModule(source : string, filename : AbstractPath, // vue-loader next: https://github.com/vuejs/vue-loader/blob/next/src/index.ts#L91 const { descriptor, errors } = sfc_parse(source, { filename: strFilename, - sourceMap: genSourcemap, + sourceMap: !!genSourcemap, }); @@ -111,13 +115,14 @@ export async function createSFCModule(source : string, filename : AbstractPath, const compileTemplateOptions : SFCTemplateCompileOptions|undefined = descriptor.template ? { // hack, since sourceMap is not configurable an we want to get rid of source-map dependency. see genSourcemap - compiler: { ...vue_CompilerDOM, compile: (template, opts) => vue_CompilerDOM.compile(template, { ...opts, sourceMap: genSourcemap }) }, + compiler: { ...vue_CompilerDOM, compile: (template, options) => vue_CompilerDOM.compile(template, { ...options, sourceMap: !!genSourcemap }) }, source: descriptor.template.src ? (await (await getResource({ refPath: filename, relPath: descriptor.template.src }, options).getContent()).getContentData(false)) as string : descriptor.template.content, filename: descriptor.filename, isProd, scoped: hasScoped, id: scopeId, slotted: descriptor.slotted, + inMap: descriptor.template.map, compilerOptions: { isCustomElement, whitespace, @@ -170,7 +175,7 @@ export async function createSFCModule(source : string, filename : AbstractPath, // src: https://github.com/vuejs/vue-next/blob/15baaf14f025f6b1d46174c9713a2ec517741d0d/packages/compiler-sfc/src/compileScript.ts#L43 const scriptBlock = sfc_compileScript(descriptor, { isProd, - sourceMap: genSourcemap, + sourceMap: !!genSourcemap, id: scopeId, // @ts-ignore (unstable resolution: node_modules/@babel/parser/typings/babel-parser vs node_modules/@types/babel__core/node_modules/@babel/parser/typings/babel-parser) babelParserPlugins: [ ...contextBabelParserPlugins, ...additionalBabelParserPlugins ], // [...babelParserDefaultPlugins, 'jsx'] + additionalBabelParserPlugins // babelParserDefaultPlugins = [ 'bigInt', 'optionalChaining', 'nullishCoalescingOperator' ] @@ -180,6 +185,9 @@ export async function createSFCModule(source : string, filename : AbstractPath, templateOptions: compileTemplateOptions, }); + if(scriptBlock.map) + scriptBlock.content += `\r\n${buildMapComment(scriptBlock.map)}` + // note: // scriptBlock.content is the script code after vue transformations // scriptBlock.scriptAst is the script AST before vue transformations @@ -192,7 +200,7 @@ export async function createSFCModule(source : string, filename : AbstractPath, compileTemplateOptions.compilerOptions.bindingMetadata = bindingMetadata; await loadDeps(filename, depsList, options); - Object.assign(component, interopRequireDefault(createCJSModule(filename, transformedScriptSource, options).exports).default); + Object.assign(component, interopRequireDefault((await createCJSModule(filename, transformedScriptSource, options)).exports).default); } @@ -237,11 +245,14 @@ export async function createSFCModule(source : string, filename : AbstractPath, for ( const err of template.tips ) log?.('info', 'SFC template', err); + if(template.map) + template.code += `\r\n${buildMapComment(template.map)}` + return await transformJSCode(template.code, true, descriptor.filename, additionalBabelParserPlugins, additionalBabelPlugins, log, devMode); }); await loadDeps(filename, templateDepsList, options); - Object.assign(component, createCJSModule(filename, templateTransformedSource, options).exports); + Object.assign(component, (await createCJSModule(filename, templateTransformedSource, options)).exports); } diff --git a/src/tools.ts b/src/tools.ts index fc7cb57..2b440ba 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -44,7 +44,7 @@ import { createSFCModule } from './createSFCModule' /** * @internal */ -const genSourcemap : boolean = !!process.env.GEN_SOURCEMAP; +const genSourcemap : boolean | "inline" = process.env.GEN_SOURCEMAP; const version : string = process.env.VERSION as string; @@ -253,7 +253,7 @@ export async function transformJSCode(source : string, moduleSourceType : boolea comments: devMode, retainLines: devMode, //envName: devMode ? 'development' : 'production', see 'process.env.BABEL_ENV': JSON.stringify(mode), - + filename: filename.toString(), //minified, sourceType: moduleSourceType ? 'module' : 'script', }); @@ -324,7 +324,7 @@ export async function loadModuleInternal(pathCx : PathContext, options : Options * Create a cjs module * @internal */ -export function defaultCreateCJSModule(refPath : AbstractPath, source : string, options : Options) : Module { +export async function defaultCreateCJSModule(refPath : AbstractPath, source : string, options : Options) : Promise { const { moduleCache, pathResolve, getResource } = options; @@ -348,8 +348,31 @@ export function defaultCreateCJSModule(refPath : AbstractPath, source : string, // see https://github.com/nodejs/node/blob/a46b21f556a83e43965897088778ddc7d46019ae/lib/internal/modules/cjs/loader.js#L195-L198 // see https://github.com/nodejs/node/blob/a46b21f556a83e43965897088778ddc7d46019ae/lib/internal/modules/cjs/loader.js#L1102 - const moduleFunction = Function('exports', 'require', 'module', '__filename', '__dirname', '__vsfcl_import__', source); - moduleFunction.call(module.exports, module.exports, require, module, refPath, pathResolve({ refPath, relPath: '.' }, options), importFunction); + const wrappedSource = `(exports, require, module, __filename, __dirname, __vsfcl_import__) => { ${source} \r\n }` + + let ast = babel_parse(wrappedSource, { + sourceType: 'module', + sourceFilename: refPath.toString(), + }); + + const transformedScript = await babel_transformFromAstAsync(ast, wrappedSource, { + sourceMaps: "inline", + babelrc: false, + configFile: false, + highlightCode: false, + filename: refPath.toString(), + sourceType: 'module', + }); + + if ( transformedScript === null || transformedScript.code == null ) { // == null or undefined + + const msg = `unable to transform script "${refPath.toString()}"`; + options.log?.('error', msg); + throw new Error(msg) + } + + + eval(transformedScript.code)(module.exports, require, module, refPath, pathResolve({ refPath, relPath: '.' }, options), importFunction) return module; } @@ -379,7 +402,7 @@ export async function createJSModule(source : string, moduleSourceType : boolean }); await loadDeps(filename, depsList, options); - return createCJSModule(filename, transformedSource, options).exports; + return (await createCJSModule(filename, transformedSource, options)).exports; } diff --git a/src/types.ts b/src/types.ts index dcdecdb..f8a130f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -377,7 +377,7 @@ export type Options = { * creates a CommonJS module from JS source string. * *(optional)* */ - createCJSModule(refPath : AbstractPath, source : string, options : Options) : Module, + createCJSModule(refPath : AbstractPath, source : string, options : Options) : Promise,