This repository has been archived by the owner on Feb 17, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
562 additions
and
178 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// vue compiler module for transforming `<tag>:<attribute>` to `require` | ||
|
||
import { urlToRequire, ASTNode, Attr } from './utils' | ||
|
||
export interface AssetURLOptions { | ||
[name: string]: string | string[] | ||
} | ||
|
||
export interface TransformAssetUrlsOptions { | ||
/** | ||
* @deprecated | ||
* If base is provided, instead of transforming relative asset urls into | ||
* imports, they will be directly rewritten to absolute urls. | ||
*/ | ||
base?: string | ||
forceRequire?: boolean | ||
} | ||
|
||
const defaultOptions: AssetURLOptions = { | ||
audio: 'src', | ||
video: ['src', 'poster'], | ||
source: 'src', | ||
img: 'src', | ||
image: ['xlink:href', 'href'], | ||
use: ['xlink:href', 'href'], | ||
} | ||
|
||
export default ( | ||
userOptions?: AssetURLOptions, | ||
transformAssetUrlsOption?: TransformAssetUrlsOptions | ||
) => { | ||
const options = userOptions | ||
? Object.assign({}, defaultOptions, userOptions) | ||
: defaultOptions | ||
|
||
return { | ||
postTransformNode: (node: ASTNode) => { | ||
transform(node, options, transformAssetUrlsOption) | ||
}, | ||
} | ||
} | ||
|
||
function transform( | ||
node: ASTNode, | ||
options: AssetURLOptions, | ||
transformAssetUrlsOption?: TransformAssetUrlsOptions | ||
) { | ||
for (const tag in options) { | ||
if ((tag === '*' || node.tag === tag) && node.attrs) { | ||
const attributes = options[tag] | ||
if (typeof attributes === 'string') { | ||
node.attrs.some((attr) => | ||
rewrite(attr, attributes, transformAssetUrlsOption) | ||
) | ||
} else if (Array.isArray(attributes)) { | ||
attributes.forEach((item) => | ||
node.attrs.some((attr) => | ||
rewrite(attr, item, transformAssetUrlsOption) | ||
) | ||
) | ||
} | ||
} | ||
} | ||
} | ||
|
||
function rewrite( | ||
attr: Attr, | ||
name: string, | ||
transformAssetUrlsOption?: TransformAssetUrlsOptions | ||
) { | ||
if (attr.name === name) { | ||
const value = attr.value | ||
// only transform static URLs | ||
if (value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') { | ||
attr.value = urlToRequire(value.slice(1, -1), transformAssetUrlsOption) | ||
return true | ||
} | ||
} | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
import { | ||
VueTemplateCompiler, | ||
VueTemplateCompilerOptions, | ||
ErrorWithRange, | ||
} from './types' | ||
|
||
import assetUrlsModule, { | ||
AssetURLOptions, | ||
TransformAssetUrlsOptions, | ||
} from './assetUrl' | ||
import srcsetModule from './srcset' | ||
|
||
const consolidate = require('consolidate') | ||
const transpile = require('vue-template-es2015-compiler') | ||
|
||
export interface TemplateCompileOptions { | ||
source: string | ||
filename: string | ||
compiler: VueTemplateCompiler | ||
compilerOptions?: VueTemplateCompilerOptions | ||
transformAssetUrls?: AssetURLOptions | boolean | ||
transformAssetUrlsOptions?: TransformAssetUrlsOptions | ||
preprocessLang?: string | ||
preprocessOptions?: any | ||
transpileOptions?: any | ||
isProduction?: boolean | ||
isFunctional?: boolean | ||
optimizeSSR?: boolean | ||
prettify?: boolean | ||
} | ||
|
||
export interface TemplateCompileResult { | ||
ast: Object | undefined | ||
code: string | ||
source: string | ||
tips: (string | ErrorWithRange)[] | ||
errors: (string | ErrorWithRange)[] | ||
} | ||
|
||
export function compileTemplate( | ||
options: TemplateCompileOptions | ||
): TemplateCompileResult { | ||
const { preprocessLang } = options | ||
const preprocessor = preprocessLang && consolidate[preprocessLang] | ||
if (preprocessor) { | ||
return actuallyCompile( | ||
Object.assign({}, options, { | ||
source: preprocess(options, preprocessor), | ||
}) | ||
) | ||
} else if (preprocessLang) { | ||
return { | ||
ast: {}, | ||
code: `var render = function () {}\n` + `var staticRenderFns = []\n`, | ||
source: options.source, | ||
tips: [ | ||
`Component ${options.filename} uses lang ${preprocessLang} for template. Please install the language preprocessor.`, | ||
], | ||
errors: [ | ||
`Component ${options.filename} uses lang ${preprocessLang} for template, however it is not installed.`, | ||
], | ||
} | ||
} else { | ||
return actuallyCompile(options) | ||
} | ||
} | ||
|
||
function preprocess( | ||
options: TemplateCompileOptions, | ||
preprocessor: any | ||
): string { | ||
const { source, filename, preprocessOptions } = options | ||
|
||
const finalPreprocessOptions = Object.assign( | ||
{ | ||
filename, | ||
}, | ||
preprocessOptions | ||
) | ||
|
||
// Consolidate exposes a callback based API, but the callback is in fact | ||
// called synchronously for most templating engines. In our case, we have to | ||
// expose a synchronous API so that it is usable in Jest transforms (which | ||
// have to be sync because they are applied via Node.js require hooks) | ||
let res: any, err | ||
preprocessor.render( | ||
source, | ||
finalPreprocessOptions, | ||
(_err: Error | null, _res: string) => { | ||
if (_err) err = _err | ||
res = _res | ||
} | ||
) | ||
|
||
if (err) throw err | ||
return res | ||
} | ||
|
||
function actuallyCompile( | ||
options: TemplateCompileOptions | ||
): TemplateCompileResult { | ||
const { | ||
source, | ||
compiler, | ||
compilerOptions = {}, | ||
transpileOptions = {}, | ||
transformAssetUrls, | ||
transformAssetUrlsOptions, | ||
isProduction = process.env.NODE_ENV === 'production', | ||
isFunctional = false, | ||
optimizeSSR = false, | ||
prettify = true, | ||
} = options | ||
|
||
const compile = | ||
optimizeSSR && compiler.ssrCompile ? compiler.ssrCompile : compiler.compile | ||
|
||
let finalCompilerOptions = compilerOptions | ||
if (transformAssetUrls) { | ||
const builtInModules = [ | ||
transformAssetUrls === true | ||
? assetUrlsModule(undefined, transformAssetUrlsOptions) | ||
: assetUrlsModule(transformAssetUrls, transformAssetUrlsOptions), | ||
srcsetModule(transformAssetUrlsOptions), | ||
] | ||
finalCompilerOptions = Object.assign({}, compilerOptions, { | ||
modules: [...builtInModules, ...(compilerOptions.modules || [])], | ||
filename: options.filename, | ||
}) | ||
} | ||
|
||
const { ast, render, staticRenderFns, tips, errors } = compile( | ||
source, | ||
finalCompilerOptions | ||
) | ||
|
||
if (errors && errors.length) { | ||
return { | ||
ast, | ||
code: `var render = function () {}\n` + `var staticRenderFns = []\n`, | ||
source, | ||
tips, | ||
errors, | ||
} | ||
} else { | ||
const finalTranspileOptions = Object.assign({}, transpileOptions, { | ||
transforms: Object.assign({}, transpileOptions.transforms, { | ||
stripWithFunctional: isFunctional, | ||
}), | ||
}) | ||
|
||
const toFunction = (code: string): string => { | ||
return `function (${isFunctional ? `_h,_vm` : ``}) {${code}}` | ||
} | ||
|
||
// transpile code with vue-template-es2015-compiler, which is a forked | ||
// version of Buble that applies ES2015 transforms + stripping `with` usage | ||
let code = | ||
transpile( | ||
`var __render__ = ${toFunction(render)}\n` + | ||
`var __staticRenderFns__ = [${staticRenderFns.map(toFunction)}]`, | ||
finalTranspileOptions | ||
) + `\n` | ||
|
||
// #23 we use __render__ to avoid `render` not being prefixed by the | ||
// transpiler when stripping with, but revert it back to `render` to | ||
// maintain backwards compat | ||
code = code.replace(/\s__(render|staticRenderFns)__\s/g, ' $1 ') | ||
|
||
if (!isProduction) { | ||
// mark with stripped (this enables Vue to use correct runtime proxy | ||
// detection) | ||
code += `render._withStripped = true` | ||
|
||
if (prettify) { | ||
try { | ||
code = require('prettier').format(code, { | ||
semi: false, | ||
parser: 'babel', | ||
}) | ||
} catch (e) { | ||
if (e.code === 'MODULE_NOT_FOUND') { | ||
tips.push( | ||
'The `prettify` option is on, but the dependency `prettier` is not found.\n' + | ||
'Please either turn off `prettify` or manually install `prettier`.' | ||
) | ||
} | ||
tips.push( | ||
`Failed to prettify component ${options.filename} template source after compilation.` | ||
) | ||
} | ||
} | ||
} | ||
|
||
return { | ||
ast, | ||
code, | ||
source, | ||
tips, | ||
errors, | ||
} | ||
} | ||
} |
Oops, something went wrong.