import { parse as parseQuery } from 'querystring' ; import webpack from 'webpack' ; import { isFunction , isObject , isRegExp , isString } from '@intlify/shared' ; const Dependency = require ( 'webpack/lib/Dependency' ) ; const NullFactory = require ( 'webpack/lib/NullFactory' ) ; const PLUGIN_ID = 'IntlifyVuePlugin' ; type InjectionValues = Record < string , any > ; class VueComponentDependency extends Dependency { static Template : VueComponentDependencyTemplate ; script ? : any ; template ? : any ; values : InjectionValues ; statement : any ; constructor ( script , template , values , statement ) { super ( ) ; this . script = script ; this . template = template ; this . values = values ; this . statement = statement ; } get type ( ) : string { return 'harmony export expression' ; } getExports ( moduleGraph ) { return { exports : [ 'default' ] , dependencies : undefined } ; } updateHash ( hash , context ) { super . updateHash ( hash , context ) ; const scriptModule = this . script ; hash . update ( ( scriptModule && ( ! scriptModule . buildMeta || scriptModule . buildMeta . exportsType ) ) + '' ) ; hash . update ( ( scriptModule && scriptModule . id ) + '' ) ; const templateModule = this . template ; hash . update ( ( templateModule && ( ! templateModule . buildMeta || templateModule . buildMeta . exportsType ) ) + '' ) ; hash . update ( ( templateModule && templateModule . id ) + '' ) ; } } function stringifyObj ( obj ) { return `Object({${ Object . keys ( obj ) . map ( key => { const code = obj [ key ] ; return `${ JSON . stringify ( key ) }:${ toCode ( code ) }` ; } ) . join ( ',' ) }})` ; } function toCode ( code ) { if ( code === null ) { return 'null' ; } if ( code === undefined ) { return 'undefined' ; } if ( isString ( code ) ) { return JSON . stringify ( code ) ; } if ( isRegExp ( code ) && code . toString ) { return code . toString ( ) ; } if ( isFunction ( code ) && code . toString ) { return '(' + code . toString ( ) + ')' ; } if ( isObject ( code ) ) { return stringifyObj ( code ) ; } return code + '' ; } function generateCode ( dep , importVar ) { const injectionCodes = [ '' ] ; Object . keys ( dep . values ) . forEach ( key => { const code = dep . values [ key ] ; if ( isFunction ( code ) ) { injectionCodes . push ( `${ importVar }.${ key } = ${ JSON . stringify ( code ( dep ) ) }` ) ; } else { injectionCodes . push ( `${ importVar }.${ key } = ${ toCode ( code ) }` ) ; } } ) ; let ret = injectionCodes . join ( '\n' ) ; ret = ret . length > 0 ? `\n${ ret }\n` : '' ; return ( ret += `/* harmony default export */ __webpack_exports__["default"] = (${ importVar });` ) ; } class VueComponentDependencyTemplate { apply ( dep , source ) { const repleacements = source . getReplacements ( ) ; const orgReplace = repleacements [ repleacements . length - 1 ] ; if ( dep . statement . declaration . start !== orgReplace . start ) { return ; } const code = generateCode ( dep , orgReplace . content ) ; source . replace ( orgReplace . start , orgReplace . end , code ) ; } } VueComponentDependency . Template = VueComponentDependencyTemplate ; function getScriptBlockModule ( parser ) { return parser . state . current . dependencies . find ( ( dep ) => { const req = dep . userRequest || dep . request ; if ( req && dep . originModule ) { const query = parseQuery ( req ) ; return query . type === 'script' && query . lang === 'js' ; } else { return false ; } } ) ; } function getTemplateBlockModule ( parser ) { return parser . state . current . dependencies . find ( ( dep ) => { const req = dep . userRequest || dep . request ; if ( req && dep . originModule ) { const query = parseQuery ( req ) ; return query . type === 'template' ; } else { return false ; } } ) ; } function toVueComponentDependency ( parser , values ) { return function vueComponentDependencyw ( statement ) { const dep = new VueComponentDependency ( getScriptBlockModule ( parser ) , getTemplateBlockModule ( parser ) , values , statement ) ; parser . state . current . addDependency ( dep ) ; return true ; } ; } export default class IntlifyVuePlugin { injections : InjectionValues ; constructor ( injections = { } ) { this . injections = injections ; } apply ( compiler ) { const injections = this . injections ; compiler . hooks . compilation . tap ( PLUGIN_ID , ( compilation , { normalModuleFactory } ) => { compilation . dependencyFactories . set ( VueComponentDependency , new NullFactory ( ) ) ; compilation . dependencyTemplates . set ( VueComponentDependency , new VueComponentDependency . Template ( ) ) ; const handler = ( parser ) => { parser . hooks . exportExpression . tap ( PLUGIN_ID , ( statement , declaration ) => { if ( parser . state . module . resource . endsWith ( '.vue' ) && declaration . name === 'script' ) { return toVueComponentDependency ( parser , injections ) ( statement ) ; } } ) ; } ; normalModuleFactory . hooks . parser . for ( 'javascript/auto' ) . tap ( PLUGIN_ID , handler ) ; normalModuleFactory . hooks . parser . for ( 'javascript/dynamic' ) . tap ( PLUGIN_ID , handler ) ; normalModuleFactory . hooks . parser . for ( 'javascript/esm' ) . tap ( PLUGIN_ID , handler ) ; } ) ; } }