rollup-plugin-vue
Advanced tools
Comparing version 6.0.0-beta.11 to 6.0.0
@@ -13,9 +13,13 @@ "use strict"; | ||
} | ||
const compiler_sfc_1 = require("@vue/compiler-sfc"); | ||
const fs_1 = __importDefault(require("fs")); | ||
const debug_1 = __importDefault(require("debug")); | ||
const hash_sum_1 = __importDefault(require("hash-sum")); | ||
const path_1 = require("path"); | ||
const querystring_1 = __importDefault(require("querystring")); | ||
const rollup_pluginutils_1 = require("rollup-pluginutils"); | ||
const sfc_1 = require("./sfc"); | ||
const template_1 = require("./template"); | ||
const style_1 = require("./style"); | ||
const customBlockFilter_1 = require("./utils/customBlockFilter"); | ||
const descriptorCache_1 = require("./utils/descriptorCache"); | ||
const query_1 = require("./utils/query"); | ||
const sourceMap_1 = require("./utils/sourceMap"); | ||
const script_1 = require("./script"); | ||
const debug = debug_1.default('rollup-plugin-vue'); | ||
@@ -38,7 +42,7 @@ const defaultOptions = { | ||
const filter = rollup_pluginutils_1.createFilter(options.include, options.exclude); | ||
const filterCustomBlock = createCustomBlockFilter(options.customBlocks); | ||
const filterCustomBlock = customBlockFilter_1.createCustomBlockFilter(options.customBlocks); | ||
return { | ||
name: 'vue', | ||
async resolveId(id, importer) { | ||
const query = parseVuePartRequest(id); | ||
const query = query_1.parseVuePartRequest(id); | ||
if (query.vue) { | ||
@@ -50,3 +54,3 @@ if (query.src) { | ||
if (resolved) { | ||
cache.set(resolved.id, getDescriptor(importer)); | ||
descriptorCache_1.setDescriptor(resolved.id, descriptorCache_1.getDescriptor(importer)); | ||
const [, originalQuery] = id.split('?', 2); | ||
@@ -58,3 +62,3 @@ resolved.id += `?${originalQuery}`; | ||
else if (!filter(query.filename)) { | ||
return undefined; | ||
return null; | ||
} | ||
@@ -64,6 +68,6 @@ debug(`resolveId(${id})`); | ||
} | ||
return undefined; | ||
return null; | ||
}, | ||
load(id) { | ||
const query = parseVuePartRequest(id); | ||
const query = query_1.parseVuePartRequest(id); | ||
if (query.vue) { | ||
@@ -73,3 +77,3 @@ if (query.src) { | ||
} | ||
const descriptor = getDescriptor(query.filename); | ||
const descriptor = descriptorCache_1.getDescriptor(query.filename); | ||
if (descriptor) { | ||
@@ -79,3 +83,3 @@ const block = query.type === 'template' | ||
: query.type === 'script' | ||
? descriptor.script | ||
? script_1.getResolvedScript(descriptor, isServer) | ||
: query.type === 'style' | ||
@@ -89,3 +93,3 @@ ? descriptor.styles[query.index] | ||
code: block.content, | ||
map: normalizeSourceMap(block.map, id), | ||
map: sourceMap_1.normalizeSourceMap(block.map, id), | ||
}; | ||
@@ -95,11 +99,21 @@ } | ||
} | ||
return undefined; | ||
return null; | ||
}, | ||
async transform(code, id) { | ||
const query = parseVuePartRequest(id); | ||
const query = query_1.parseVuePartRequest(id); | ||
// *.vue file | ||
// generate an entry module that imports the actual blocks of the SFC | ||
if (!query.vue && filter(id)) { | ||
debug(`transform SFC entry (${id})`); | ||
const output = sfc_1.transformSFCEntry(code, id, options, rootContext, isProduction, isServer, filterCustomBlock, this); | ||
if (output) { | ||
debug('SFC entry code:', '\n' + output.code + '\n'); | ||
} | ||
return output; | ||
} | ||
// sub request for blocks | ||
if (query.vue) { | ||
if (!query.src && !filter(query.filename)) | ||
if (!query.src && !filter(query.filename)) { | ||
return null; | ||
const descriptor = getDescriptor(query.filename); | ||
const hasScoped = descriptor.styles.some((s) => s.scoped); | ||
} | ||
if (query.src) { | ||
@@ -109,129 +123,11 @@ this.addWatchFile(query.filename); | ||
if (query.type === 'template') { | ||
debug(`transform(${id})`); | ||
const block = descriptor.template; | ||
const preprocessLang = block.lang; | ||
const preprocessOptions = preprocessLang && | ||
options.templatePreprocessOptions && | ||
options.templatePreprocessOptions[preprocessLang]; | ||
const result = compiler_sfc_1.compileTemplate({ | ||
filename: query.filename, | ||
source: code, | ||
inMap: query.src ? undefined : block.map, | ||
preprocessLang, | ||
preprocessOptions, | ||
preprocessCustomRequire: options.preprocessCustomRequire, | ||
compiler: options.compiler, | ||
ssr: isServer, | ||
compilerOptions: { | ||
...options.compilerOptions, | ||
scopeId: hasScoped ? `data-v-${query.id}` : undefined, | ||
bindingMetadata: descriptor.script | ||
? descriptor.script.bindings | ||
: undefined, | ||
}, | ||
transformAssetUrls: options.transformAssetUrls, | ||
}); | ||
if (result.errors.length) { | ||
result.errors.forEach((error) => this.error(typeof error === 'string' | ||
? { id: query.filename, message: error } | ||
: createRollupError(query.filename, error))); | ||
return null; | ||
} | ||
if (result.tips.length) { | ||
result.tips.forEach((tip) => this.warn({ | ||
id: query.filename, | ||
message: tip, | ||
})); | ||
} | ||
return { | ||
code: result.code, | ||
map: normalizeSourceMap(result.map, id), | ||
}; | ||
debug(`transform template (${id})`); | ||
return template_1.transformTemplate(code, id, options, query, this); | ||
} | ||
else if (query.type === 'style') { | ||
debug(`transform(${id})`); | ||
const block = descriptor.styles[query.index]; | ||
let preprocessOptions = options.preprocessOptions || {}; | ||
const preprocessLang = (options.preprocessStyles | ||
? block.lang | ||
: undefined); | ||
if (preprocessLang) { | ||
preprocessOptions = | ||
preprocessOptions[preprocessLang] || preprocessOptions; | ||
// include node_modules for imports by default | ||
switch (preprocessLang) { | ||
case 'scss': | ||
case 'sass': | ||
preprocessOptions = { | ||
includePaths: ['node_modules'], | ||
...preprocessOptions, | ||
}; | ||
break; | ||
case 'less': | ||
case 'stylus': | ||
preprocessOptions = { | ||
paths: ['node_modules'], | ||
...preprocessOptions, | ||
}; | ||
} | ||
} | ||
else { | ||
preprocessOptions = {}; | ||
} | ||
const result = await compiler_sfc_1.compileStyleAsync({ | ||
filename: query.filename, | ||
id: `data-v-${query.id}`, | ||
source: code, | ||
scoped: block.scoped, | ||
vars: !!block.vars, | ||
modules: !!block.module, | ||
postcssOptions: options.postcssOptions, | ||
postcssPlugins: options.postcssPlugins, | ||
modulesOptions: options.cssModulesOptions, | ||
preprocessLang, | ||
preprocessCustomRequire: options.preprocessCustomRequire, | ||
preprocessOptions, | ||
}); | ||
if (result.errors.length) { | ||
result.errors.forEach((error) => this.error({ | ||
id: query.filename, | ||
message: error.message, | ||
})); | ||
return null; | ||
} | ||
if (query.module) { | ||
return { | ||
code: `export default ${_(result.modules)}`, | ||
map: null, | ||
}; | ||
} | ||
else { | ||
return { | ||
code: result.code, | ||
map: normalizeSourceMap(result.map, id), | ||
}; | ||
} | ||
debug(`transform style (${id})`); | ||
return style_1.transformStyle(code, id, options, query, isProduction, this); | ||
} | ||
return null; | ||
} | ||
else if (filter(id)) { | ||
debug(`transform(${id})`); | ||
const { descriptor, errors } = parseSFC(code, id, rootContext); | ||
if (errors.length) { | ||
errors.forEach((error) => this.error(createRollupError(id, error))); | ||
return null; | ||
} | ||
// module id for scoped CSS & hot-reload | ||
const output = transformVueSFC(code, id, descriptor, { rootContext, isProduction, isServer, filterCustomBlock }, options); | ||
debug('transient .vue file:', '\n' + output + '\n'); | ||
return { | ||
code: output, | ||
map: { | ||
mappings: '', | ||
}, | ||
}; | ||
} | ||
else { | ||
return null; | ||
} | ||
return null; | ||
}, | ||
@@ -241,249 +137,3 @@ }; | ||
exports.default = PluginVue; | ||
function createCustomBlockFilter(queries) { | ||
if (!queries || queries.length === 0) | ||
return () => false; | ||
const allowed = new Set(queries.filter((query) => /^[a-z]/i.test(query))); | ||
const disallowed = new Set(queries | ||
.filter((query) => /^![a-z]/i.test(query)) | ||
.map((query) => query.substr(1))); | ||
const allowAll = queries.includes('*') || !queries.includes('!*'); | ||
return (type) => { | ||
if (allowed.has(type)) | ||
return true; | ||
if (disallowed.has(type)) | ||
return true; | ||
return allowAll; | ||
}; | ||
} | ||
function parseVuePartRequest(id) { | ||
const [filename, query] = id.split('?', 2); | ||
if (!query) | ||
return { vue: false, filename }; | ||
const raw = querystring_1.default.parse(query); | ||
if ('vue' in raw) { | ||
return { | ||
...raw, | ||
filename, | ||
vue: true, | ||
index: Number(raw.index), | ||
src: 'src' in raw, | ||
scoped: 'scoped' in raw, | ||
}; | ||
} | ||
return { vue: false, filename }; | ||
} | ||
const cache = new Map(); | ||
function getDescriptor(id) { | ||
if (cache.has(id)) { | ||
return cache.get(id); | ||
} | ||
throw new Error(`${id} is not parsed yet`); | ||
} | ||
function parseSFC(code, id, sourceRoot) { | ||
const { descriptor, errors } = compiler_sfc_1.parse(code, { | ||
sourceMap: true, | ||
filename: id, | ||
sourceRoot: sourceRoot, | ||
}); | ||
cache.set(id, descriptor); | ||
return { descriptor, errors: errors }; | ||
} | ||
function transformVueSFC(code, resourcePath, descriptor, { rootContext, isProduction, isServer, filterCustomBlock, }, options) { | ||
const shortFilePath = path_1.relative(rootContext, resourcePath) | ||
.replace(/^(\.\.[\/\\])+/, '') | ||
.replace(/\\/g, '/'); | ||
const id = hash_sum_1.default(isProduction ? shortFilePath + '\n' + code : shortFilePath); | ||
// feature information | ||
const hasScoped = descriptor.styles.some((s) => s.scoped); | ||
const templateImport = !descriptor.template | ||
? '' | ||
: getTemplateCode(descriptor, resourcePath, id, hasScoped, isServer); | ||
const renderReplace = !descriptor.template | ||
? '' | ||
: isServer | ||
? `script.ssrRender = ssrRender` | ||
: `script.render = render`; | ||
const scriptImport = getScriptCode(descriptor, resourcePath); | ||
const stylesCode = getStyleCode(descriptor, resourcePath, id, options.preprocessStyles); | ||
const customBlocksCode = getCustomBlock(descriptor, resourcePath, filterCustomBlock); | ||
const output = [ | ||
scriptImport, | ||
templateImport, | ||
stylesCode, | ||
customBlocksCode, | ||
renderReplace, | ||
]; | ||
if (hasScoped) { | ||
output.push(`script.__scopeId = ${_(`data-v-${id}`)}`); | ||
} | ||
if (!isProduction) { | ||
output.push(`script.__file = ${_(shortFilePath)}`); | ||
} | ||
else if (options.exposeFilename) { | ||
output.push(`script.__file = ${_(path_1.basename(shortFilePath))}`); | ||
} | ||
output.push('export default script'); | ||
return output.join('\n'); | ||
} | ||
function getTemplateCode(descriptor, resourcePath, id, hasScoped, isServer) { | ||
const renderFnName = isServer ? 'ssrRender' : 'render'; | ||
let templateImport = `const ${renderFnName} = () => {}`; | ||
let templateRequest; | ||
if (descriptor.template) { | ||
const src = descriptor.template.src || resourcePath; | ||
const idQuery = `&id=${id}`; | ||
const scopedQuery = hasScoped ? `&scoped=true` : ``; | ||
const srcQuery = descriptor.template.src ? `&src` : ``; | ||
const attrsQuery = attrsToQuery(descriptor.template.attrs, 'js', true); | ||
const query = `?vue&type=template${idQuery}${srcQuery}${scopedQuery}${attrsQuery}`; | ||
templateRequest = _(src + query); | ||
templateImport = `import { ${renderFnName} } from ${templateRequest}`; | ||
} | ||
return templateImport; | ||
} | ||
function getScriptCode(descriptor, resourcePath) { | ||
let scriptImport = `const script = {}`; | ||
if (descriptor.script || descriptor.scriptSetup) { | ||
if (compiler_sfc_1.compileScript) { | ||
descriptor.script = compiler_sfc_1.compileScript(descriptor); | ||
} | ||
if (descriptor.script) { | ||
const src = descriptor.script.src || resourcePath; | ||
const attrsQuery = attrsToQuery(descriptor.script.attrs, 'js'); | ||
const srcQuery = descriptor.script.src ? `&src` : ``; | ||
const query = `?vue&type=script${srcQuery}${attrsQuery}`; | ||
const scriptRequest = _(src + query); | ||
scriptImport = | ||
`import script from ${scriptRequest}\n` + | ||
`export * from ${scriptRequest}`; // support named exports | ||
} | ||
} | ||
return scriptImport; | ||
} | ||
function getStyleCode(descriptor, resourcePath, id, preprocessStyles) { | ||
let stylesCode = ``; | ||
let hasCSSModules = false; | ||
if (descriptor.styles.length) { | ||
descriptor.styles.forEach((style, i) => { | ||
const src = style.src || resourcePath; | ||
// do not include module in default query, since we use it to indicate | ||
// that the module needs to export the modules json | ||
const attrsQuery = attrsToQuery(style.attrs, 'css', preprocessStyles); | ||
const attrsQueryWithoutModule = attrsQuery.replace(/&module(=true|=[^&]+)?/, ''); | ||
// make sure to only pass id when necessary so that we don't inject | ||
// duplicate tags when multiple components import the same css file | ||
const idQuery = style.scoped ? `&id=${id}` : ``; | ||
const srcQuery = style.src ? `&src` : ``; | ||
const query = `?vue&type=style&index=${i}${srcQuery}${idQuery}`; | ||
const styleRequest = src + query + attrsQuery; | ||
const styleRequestWithoutModule = src + query + attrsQueryWithoutModule; | ||
if (style.module) { | ||
if (!hasCSSModules) { | ||
stylesCode += `\nconst cssModules = script.__cssModules = {}`; | ||
hasCSSModules = true; | ||
} | ||
stylesCode += genCSSModulesCode(id, i, styleRequest, styleRequestWithoutModule, style.module); | ||
} | ||
else { | ||
stylesCode += `\nimport ${_(styleRequest)}`; | ||
} | ||
// TODO SSR critical CSS collection | ||
}); | ||
} | ||
return stylesCode; | ||
} | ||
function getCustomBlock(descriptor, resourcePath, filter) { | ||
let code = ''; | ||
descriptor.customBlocks.forEach((block, index) => { | ||
if (filter(block.type)) { | ||
const src = block.src || resourcePath; | ||
const attrsQuery = attrsToQuery(block.attrs, block.type); | ||
const srcQuery = block.src ? `&src` : ``; | ||
const query = `?vue&type=${block.type}&index=${index}${srcQuery}${attrsQuery}`; | ||
const request = _(src + query); | ||
code += `import block${index} from ${request}\n`; | ||
code += `if (typeof block${index} === 'function') block${index}(script)\n`; | ||
} | ||
}); | ||
return code; | ||
} | ||
function createRollupError(id, error) { | ||
if ('code' in error) { | ||
return { | ||
id, | ||
plugin: 'vue', | ||
pluginCode: String(error.code), | ||
message: error.message, | ||
frame: error.loc.source, | ||
parserError: error, | ||
loc: error.loc | ||
? { | ||
file: id, | ||
line: error.loc.start.line, | ||
column: error.loc.start.column, | ||
} | ||
: undefined, | ||
}; | ||
} | ||
else { | ||
return { | ||
id, | ||
plugin: 'vue', | ||
message: error.message, | ||
parserError: error, | ||
}; | ||
} | ||
} | ||
// these are built-in query parameters so should be ignored | ||
// if the user happen to add them as attrs | ||
const ignoreList = ['id', 'index', 'src', 'type', 'lang']; | ||
function attrsToQuery(attrs, langFallback, forceLangFallback = false) { | ||
let query = ``; | ||
for (const name in attrs) { | ||
const value = attrs[name]; | ||
if (!ignoreList.includes(name)) { | ||
query += `&${querystring_1.default.escape(name)}${value ? `=${querystring_1.default.escape(String(value))}` : ``}`; | ||
} | ||
} | ||
if (langFallback || attrs.lang) { | ||
query += | ||
`lang` in attrs | ||
? forceLangFallback | ||
? `&lang.${langFallback}` | ||
: `&lang.${attrs.lang}` | ||
: `&lang.${langFallback}`; | ||
} | ||
return query; | ||
} | ||
function _(any) { | ||
return JSON.stringify(any); | ||
} | ||
function normalizeSourceMap(map, id) { | ||
if (!map) | ||
return null; | ||
if (!id.includes('type=script')) { | ||
map.file = id; | ||
map.sources[0] = id; | ||
} | ||
return { | ||
...map, | ||
version: Number(map.version), | ||
mappings: typeof map.mappings === 'string' ? map.mappings : '', | ||
}; | ||
} | ||
function genCSSModulesCode( | ||
// @ts-ignore | ||
id, index, request, requestWithoutModule, moduleName) { | ||
const styleVar = `style${index}`; | ||
let code = | ||
// first import the CSS for extraction | ||
`\nimport ${_(requestWithoutModule)}` + | ||
// then import the json file to expose to component... | ||
`\nimport ${styleVar} from ${_(request + '.js')}`; | ||
// inject variable | ||
const name = typeof moduleName === 'string' ? moduleName : '$style'; | ||
code += `\ncssModules["${name}"] = ${styleVar}`; | ||
return code; | ||
} | ||
// overwrite for cjs require('rollup-plugin-vue')() usage | ||
module.exports = PluginVue; |
{ | ||
"name": "rollup-plugin-vue", | ||
"version": "6.0.0-beta.11", | ||
"version": "6.0.0", | ||
"license": "MIT", | ||
@@ -17,3 +17,3 @@ "main": "dist/index.js", | ||
"test:unit": "jest", | ||
"test:e2e": "jest --config jest.e2e.config.js" | ||
"test:e2e": "jest --testMatch '**/*.e2e.ts'" | ||
}, | ||
@@ -31,5 +31,6 @@ "dependencies": { | ||
"@types/debug": "^4.1.5", | ||
"@types/hash-sum": "^1.0.0", | ||
"@types/jest": "^25.2.3", | ||
"@types/node": "^13.13.2", | ||
"@vue/compiler-sfc": "^3.0.0-rc.5", | ||
"@vue/compiler-sfc": "^3.0.3", | ||
"husky": "^4.2.0", | ||
@@ -36,0 +37,0 @@ "jest": "^26.0.1", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
39348
33
932
1
15
6
1