diff --git a/examples/vue-mp/src/pages/counter/index.js b/examples/vue-mp/src/pages/counter/index.js deleted file mode 100644 index 56dcdfe..0000000 --- a/examples/vue-mp/src/pages/counter/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import Counter from './Counter' -import Vue from 'vue' - -const app = new Vue( Counter ) - -app.$mount() diff --git a/examples/vue-mp/src/pages/counter/Counter.vue b/examples/vue-mp/src/pages/counter/index.vue similarity index 95% rename from examples/vue-mp/src/pages/counter/Counter.vue rename to examples/vue-mp/src/pages/counter/index.vue index de72806..8893c78 100644 --- a/examples/vue-mp/src/pages/counter/Counter.vue +++ b/examples/vue-mp/src/pages/counter/index.vue @@ -56,3 +56,9 @@ export default { font-size: 29px; } + + + { + "a": 1 + } + diff --git a/examples/vue-mp/webpack.config.js b/examples/vue-mp/webpack.config.js index 27cdca2..e82c554 100644 --- a/examples/vue-mp/webpack.config.js +++ b/examples/vue-mp/webpack.config.js @@ -16,7 +16,7 @@ module.exports = { entry: { 'app': path.resolve( __dirname, 'src/index.js' ), 'package/pages/demo/index': path.resolve( __dirname, 'src/package/pages/demo/index.js' ), - 'pages/counter/index': path.resolve( __dirname, 'src/pages/counter/index.js' ), + 'pages/counter/index': path.resolve( __dirname, 'src/pages/counter/index.vue' ), 'pages/todomvc/index': path.resolve( __dirname, 'src/pages/todomvc/index.js' ), }, diff --git a/packages/target/lib/frameworks/shared/plugin/index.js b/packages/target/lib/frameworks/shared/plugin/index.js index ca32505..390299f 100644 --- a/packages/target/lib/frameworks/shared/plugin/index.js +++ b/packages/target/lib/frameworks/shared/plugin/index.js @@ -18,9 +18,22 @@ class FrameworkPlugin { frameworkLoaderRegexp, compiler: templateCompiler, entryLoader, - pitcherLoader + pitcherLoader, } = this.options || {} + // .[framework] maybe used as entry + if ( entryLoader ) { + hookFrameworkEntry( { + rules, + files: sfcFiles, + frameworkLoaderRegexp, + entryLoader: { + options: {}, + loader: entryLoader, + }, + } ) + } + // generate components replaceTemplateCompiler( { rules, @@ -40,6 +53,21 @@ class FrameworkPlugin { } } +function hookFrameworkEntry( { rules, files = {}, frameworkLoaderRegexp, entryLoader } ) { + const entryRule = findRuleByFile( rules, files ) + + if ( !entryRule ) { + return + } + + const entryUse = entryRule.use + const vueUseLoaderIndex = entryUse.findIndex( u => { + return frameworkLoaderRegexp.test( u.loader ) + } ) + + entryUse.splice( vueUseLoaderIndex, 0, entryLoader ) +} + // add our loader before [framework]-loader function replaceTemplateCompiler( { rules, compiler, files = [], frameworkLoaderRegexp } ) { const rule = findRuleByFile( rules, files ) diff --git a/packages/target/lib/frameworks/shared/utils/extractPageFromScriptSource.js b/packages/target/lib/frameworks/shared/utils/extractPageFromScriptSource.js new file mode 100644 index 0000000..fee9330 --- /dev/null +++ b/packages/target/lib/frameworks/shared/utils/extractPageFromScriptSource.js @@ -0,0 +1,27 @@ +const { babel } = require( '../../../utils/babel' ) +const extractConfigPlugin = require( '../../../babel-plugins/extract-config' ) +const entryComponentPlugin = require( '../../../babel-plugins/entry-component' ) +const mpTypePlugin = require( '../../../babel-plugins/mptype' ) +const resolveSource = require( '../../../utils/resolveSource' ) +const hashify = require( '../../../utils/hashify' ) +const removeExtension = require( '../../../utils/removeExtension' ) + +module.exports = function ( source, loaderContext ) { + const entryHelper = loaderContext.megaloEntryHelper + + const resourcePath = removeExtension( loaderContext.resourcePath, '.js' ) + + if ( entryHelper.isEntry( resourcePath ) ) { + const entryKey = entryHelper.getEntryKey( resourcePath ) + + return Promise.resolve( { + file: entryKey, + config: null, + entryComponent: { + name: hashify( resourcePath ), + }, + } ) + } + + return Promise.resolve( null ) +} diff --git a/packages/target/lib/frameworks/vue/loader/config.js b/packages/target/lib/frameworks/vue/loader/config.js new file mode 100644 index 0000000..2d03819 --- /dev/null +++ b/packages/target/lib/frameworks/vue/loader/config.js @@ -0,0 +1,28 @@ +const toString = Object.prototype.toString + +module.exports = function ( source ) { + const loaderContext = this + + const entryHelper = loaderContext.megaloEntryHelper + + if ( entryHelper.isEntry( loaderContext.resourcePath ) ) { + const entryKey = entryHelper.getEntryKey( loaderContext.resourcePath ) + + let config + try { + config = JSON.parse( source ) + if ( toString.call( config ) !== '[object Object]' ) { + config = {} + } + } catch ( e ) { + config = {} + } + + loaderContext.megaloCacheToPages( { + file: entryKey, + config: config, + } ) + } + + return '' +} diff --git a/packages/target/lib/frameworks/vue/loader/pitcher.js b/packages/target/lib/frameworks/vue/loader/pitcher.js index 39e5654..15bb730 100644 --- a/packages/target/lib/frameworks/vue/loader/pitcher.js +++ b/packages/target/lib/frameworks/vue/loader/pitcher.js @@ -2,9 +2,11 @@ const qs = require( 'querystring' ) const loaderUtils = require( 'loader-utils' ) const hash = require( 'hash-sum' ) const selfPath = require.resolve( 'vue-loader' ) +const vueEntryPath = require.resolve( './vue-entry' ) const styleLoaderPath = require.resolve( './style' ) const templateLoaderPath = require.resolve( './template' ) const scriptLoaderPath = require.resolve( './script' ) +const configLoaderPath = require.resolve( './config' ) const isESLintLoader = l => /(\/|\\|@)eslint-loader/.test(l.path) const isNullLoader = l => /(\/|\\|@)null-loader/.test(l.path) @@ -53,6 +55,8 @@ module.exports.pitch = function (remainingRequest) { // remove self loaders = loaders.filter(isPitcher) + // remove vue entry loader + loaders = loaders.filter( l => l.path !== vueEntryPath ) // do not inject if user uses null-loader to void the type (#1239) if (loaders.some(isNullLoader)) { @@ -140,6 +144,19 @@ module.exports.pitch = function (remainingRequest) { } } + // handle config block + if ( + query.type === 'custom' && + query.blockType === 'config' + ) { + const vueLoader = loaders.find( l => l.path = selfPath ) + const request = genRequest( [ + configLoaderPath, + ].concat( vueLoader ? [ vueLoader ] : [] ) ) + + return `import mod from ${request}; export default mod; export * from ${request}` + } + // if a custom block has no other matching loader other than vue-loader itself, // we should ignore it if (query.type === `custom` && diff --git a/packages/target/lib/frameworks/vue/loader/script.js b/packages/target/lib/frameworks/vue/loader/script.js index ae52a79..741e483 100644 --- a/packages/target/lib/frameworks/vue/loader/script.js +++ b/packages/target/lib/frameworks/vue/loader/script.js @@ -1,20 +1,33 @@ const path = require( 'path' ) const extractCompilerOptionsFromScriptSource = require( '../../shared/utils/extractCompilerOptionsFromScriptSource' ) +const extractPageFromScriptSource = + require( '../../shared/utils/extractPageFromScriptSource' ) const removeExtension = require( '../../../utils/removeExtension' ) module.exports = function ( source ) { const loaderContext = this const callback = loaderContext.async() + const realResourcePath = removeExtension( loaderContext.resourcePath, '.js' ) - let realResourcePath = removeExtension( loaderContext.resourcePath ) + const jobs = [ + extractCompilerOptionsFromScriptSource( source, loaderContext ), + extractPageFromScriptSource( source, loaderContext ), + ] + + Promise.all( jobs ) + .then( data => { + const [ compilerOptions, page ] = data || [] - extractCompilerOptionsFromScriptSource( source, loaderContext ) - .then( compilerOptions => { loaderContext.megaloCacheToAllCompilerOptions( realResourcePath, compilerOptions, ) + + if ( page ) { + loaderContext.megaloCacheToPages( page ) + } + callback( null, source ) } ) .catch( e => { diff --git a/packages/target/lib/frameworks/vue/loader/vue-entry.js b/packages/target/lib/frameworks/vue/loader/vue-entry.js new file mode 100644 index 0000000..352e850 --- /dev/null +++ b/packages/target/lib/frameworks/vue/loader/vue-entry.js @@ -0,0 +1,25 @@ +const qs = require( 'querystring' ) + +module.exports = source => source + +module.exports.pitch = function ( remainingRequest ) { + const loaderContext = this + const query = qs.parse( loaderContext.resourceQuery.slice( 1 ) ) + const entryHelper = loaderContext.megaloEntryHelper + + // handle *.[framework] as entry + if ( entryHelper.isEntry( loaderContext.resourcePath ) ) { + const entryKey = entryHelper.getEntryKey( loaderContext.resourcePath ) + + // only pitch in first time + if ( typeof query.vue === 'undefined' ) { + return ` + import Component from ${ JSON.stringify( '-!' + remainingRequest ) }; + import Vue from 'vue'; + Component.mpType = "${ entryKey === 'app' ? 'app' : 'page' }"; + const app = new Vue(Component); + app.$mount(); + ` + } + } +} diff --git a/packages/target/lib/frameworks/vue/plugin/index.js b/packages/target/lib/frameworks/vue/plugin/index.js index 90240fe..4a0c8e6 100644 --- a/packages/target/lib/frameworks/vue/plugin/index.js +++ b/packages/target/lib/frameworks/vue/plugin/index.js @@ -15,6 +15,7 @@ class VuePlugin { frameworkLoaderRegexp: /^vue-loader|(\/|\\|@)vue-loader/, pitcherLoader: require.resolve( '../loader/pitcher' ), compiler: options.compiler.vue, + entryLoader: require.resolve( '../loader/vue-entry' ) } ) ).apply( compiler ) } diff --git a/packages/target/lib/loaders/entry.js b/packages/target/lib/loaders/js-entry.js similarity index 98% rename from packages/target/lib/loaders/entry.js rename to packages/target/lib/loaders/js-entry.js index a30f259..901c55e 100644 --- a/packages/target/lib/loaders/entry.js +++ b/packages/target/lib/loaders/js-entry.js @@ -1,5 +1,4 @@ const path = require( 'path' ) -const semver = require( 'semver' ) const { babel } = require( '../utils/babel' ) const extractConfigPlugin = require( '../babel-plugins/extract-config' ) const entryComponentPlugin = require( '../babel-plugins/entry-component' ) diff --git a/packages/target/lib/platforms/wechat/codegen/page.json.js b/packages/target/lib/platforms/wechat/codegen/page.json.js index 084b9c2..ad98766 100755 --- a/packages/target/lib/platforms/wechat/codegen/page.json.js +++ b/packages/target/lib/platforms/wechat/codegen/page.json.js @@ -1,7 +1,6 @@ const composePlatformConfig = require( '../../shared/utils/composePlatformConfig' ) -module.exports = function ( { config } ) { +module.exports = function ( { config = {} } ) { const _config = composePlatformConfig( config, 'wechat' ) return JSON.stringify( _config, 0, 2 ) } - diff --git a/packages/target/lib/plugins/MegaloPlugin.js b/packages/target/lib/plugins/MegaloPlugin.js index 522c67c..ae05c35 100644 --- a/packages/target/lib/plugins/MegaloPlugin.js +++ b/packages/target/lib/plugins/MegaloPlugin.js @@ -30,12 +30,12 @@ class MegaloPlugin { replaceGlobalObject( compiler, megaloOptions ) // generate pages - hookEntry( { + hookJSEntry( { rules, files: [ 'foo.js', 'foo.ts' ], entryLoader: { options: {}, - loader: require.resolve( '../loaders/entry' ), + loader: require.resolve( '../loaders/js-entry' ), }, } ) @@ -59,7 +59,7 @@ function replaceGlobalObject( compiler, megaloOptions ) { } // [framework]-loader clones babel-loader rule, we shall ignore it -function hookEntry( { rules, files = {}, entryLoader } ) { +function hookJSEntry( { rules, files = {}, entryLoader } ) { const entryRule = findRuleByFile( rules, files ) if ( !entryRule ) { @@ -196,7 +196,22 @@ function attachCacheAPI( compiler ) { // sideEffects: true function cacheToPages( { file, config, entryComponent } = {} ) { - pages[ file ] = { file, config, entryComponent } + if ( !pages[ file ] ) { + pages[ file ] = {} + } + + if ( file ) { + pages[ file ].file = file + } + + // merge config + if ( config ) { + pages[ file ].config = Object.assign( {}, pages[ file ].config || {}, config ) + } + + if ( entryComponent ) { + pages[ file ].entryComponent = entryComponent + } } function cacheToAllCompilerOptions( resourcePath, compilerOptions = {} ) {