diff --git a/packages/adapter-webpack/package.json b/packages/adapter-webpack/package.json index 679bf57a..2ff5d559 100644 --- a/packages/adapter-webpack/package.json +++ b/packages/adapter-webpack/package.json @@ -28,7 +28,6 @@ "dependencies": { "@uiengine/util": "2.3.1", "globby": "10.0.1", - "htmlescape": "1.1.1", "object-hash": "^1.3.1", "require-from-string": "2.0.2" }, @@ -49,7 +48,8 @@ "vue-template-compiler": "2.6.10", "webpack": "4.39.2", "webpack-merge": "4.2.1", - "webpack-node-externals": "1.7.2" + "webpack-node-externals": "1.7.2", + "webpack-virtual-modules": "^0.1.11" }, "optionalDependencies": { "parse-prop-types": "0.3.0", diff --git a/packages/adapter-webpack/src/deps.js b/packages/adapter-webpack/src/deps.js index 428fe0f6..8b7039a5 100644 --- a/packages/adapter-webpack/src/deps.js +++ b/packages/adapter-webpack/src/deps.js @@ -18,12 +18,13 @@ async function getDependencyFiles (options, filePath, cache) { // cache the promises so that files do not get added multiple times const promise = new Promise((resolve, reject) => { buildQueued(options, filePath) - .then(({ serverChunk, clientChunk }) => { - const chunk = serverChunk || clientChunk + .then(({ serverComponentChunk, clientComponentChunk }) => { + const chunk = serverComponentChunk || clientComponentChunk // https://webpack.js.org/api/stats#chunk-objects - const filePaths = chunk ? chunk.modules.map(({ id }) => { - const mod = id.split('?!').pop().replace(/\?.*$/, '') + const filePaths = chunk ? chunk.modules.map(({ id, name }) => { + const ident = typeof id === 'number' ? (name.match(/\s([^\s]*)/g) || ['']).shift().trim() : id + const mod = ident && ident.split('?!').pop().replace(/\?.*$/, '') let modulePath if (mod) modulePath = mod.startsWith('.') ? path.resolve(mod) : require.resolve(mod) return modulePath && crossPlatformPath(modulePath) diff --git a/packages/adapter-webpack/src/index.js b/packages/adapter-webpack/src/index.js index 1f70ad37..102e0bfc 100644 --- a/packages/adapter-webpack/src/index.js +++ b/packages/adapter-webpack/src/index.js @@ -1,5 +1,4 @@ - -const htmlescape = require('htmlescape') +const { FileUtil: { requireUncached } } = require('@uiengine/util') const { extractDependentFiles, extractDependencyFiles } = require('./deps') const { buildSetup, buildQueued, getExtractProperties } = require('./util') @@ -18,7 +17,7 @@ async function setup (options) { } async function registerComponentFile (options, filePath) { - await buildQueued(options, filePath, true) + await buildQueued(options, filePath, undefined, true) const extractProperties = getExtractProperties(options) const [properties, dependentFiles, dependencyFiles] = await Promise.all([ @@ -37,32 +36,17 @@ async function registerComponentFile (options, filePath) { async function render (options, filePath, data = {}) { let rendered, foot - const { serverRenderPath, serverComponentPath, clientRenderPath, clientComponentPath } = await buildQueued(options, filePath, true) + const { serverResultPath, clientResultPath } = await buildQueued(options, filePath, data, true) - if (serverRenderPath && serverComponentPath) { - const ServerRender = require(serverRenderPath) - const ServerComponent = require(serverComponentPath) - const serverRender = ServerRender.default || ServerRender - const serverComponent = ServerComponent.default || ServerComponent - rendered = await serverRender(serverComponent, data) + if (serverResultPath) { + rendered = await requireUncached(serverResultPath) } - if (clientRenderPath && clientComponentPath) { - foot = ` - - - ` + if (clientResultPath) { + foot = ` ` } - return { - rendered, - foot - } + return { rendered, foot } } function filesForComponent (options, componentName) { diff --git a/packages/adapter-webpack/src/util.js b/packages/adapter-webpack/src/util.js index 808f805e..3c118d3e 100644 --- a/packages/adapter-webpack/src/util.js +++ b/packages/adapter-webpack/src/util.js @@ -2,6 +2,7 @@ const { join, relative } = require('path') const hash = require('object-hash') const webpack = require('webpack') const merge = require('webpack-merge') +const VirtualModulesPlugin = require('webpack-virtual-modules') const { DebounceUtil: { debounce } } = require('@uiengine/util') const { cacheGet, cachePut, cacheDel } = require('./cache') @@ -56,9 +57,6 @@ const buildConfig = (options, isSetupBuild = false) => { const entry = isSetupBuild ? { [`${ext}-client`]: clientRenderPath } : {} - const library = isSetupBuild - ? 'UIengineWebpack_render' - : 'UIengineWebpack_component' config.client = merge(clientConfig, { name: WEBPACK_NAME_CLIENT, @@ -67,10 +65,7 @@ const buildConfig = (options, isSetupBuild = false) => { entry, output: { path: filesDir(options), - filename: '[name].js', - library, - libraryTarget: 'window', - libraryExport: 'default' + filename: '[name].js' }, plugins: [ new webpack.DefinePlugin({ @@ -83,15 +78,52 @@ const buildConfig = (options, isSetupBuild = false) => { return config } -const addFileToQueue = (options, filePath, queue) => { +const addFileToQueue = (options, filePath, data, queue) => { if (queue.promises[filePath]) return queue.promises[filePath] - const { serverConfig, clientConfig } = options - const serverId = getFileId(filePath, 'server') - const clientId = getFileId(filePath, 'client') + const { serverConfig, serverRenderPath, clientConfig, clientRenderPath } = options + const serverComponentId = getFileId(filePath, 'server-component') + const serverResultId = getFileId(filePath, 'server-result') + const clientComponentId = getFileId(filePath, 'client-component') + const clientResultId = getFileId(filePath, 'client-result') + + if (serverConfig) { + queue.config.server.entry[serverComponentId] = filePath + + if (data) { + queue.config.server.entry[serverResultId] = `/${serverResultId}.js` + queue.config.server.plugins.push( + new VirtualModulesPlugin({ + [queue.config.server.entry[serverResultId]]: ` + const ServerRender = require('${serverRenderPath}') + const ServerComponent = require('${filePath}') + const serverRender = ServerRender.default || ServerRender + const serverComponent = ServerComponent.default || ServerComponent + + module.exports = serverRender(serverComponent, ${JSON.stringify(data)}) + ` + }) + ) + } + } + + if (clientConfig) { + queue.config.client.entry[clientComponentId] = filePath - if (serverConfig) queue.config.server.entry[serverId] = filePath - if (clientConfig) queue.config.client.entry[clientId] = filePath + if (data) { + queue.config.client.entry[clientResultId] = `/${clientResultId}.js` + queue.config.client.plugins.push( + new VirtualModulesPlugin({ + [queue.config.client.entry[clientResultId]]: ` + import clientRender from '${clientRenderPath}' + import Component from '${filePath}' + + export default clientRender(Component, ${JSON.stringify(data)}) + ` + }) + ) + } + } // cache the promises so that files do not get added multiple times const promise = new Promise((resolve, reject) => { @@ -99,8 +131,10 @@ const addFileToQueue = (options, filePath, queue) => { reject, resolve, filePath, - serverId, - clientId + serverComponentId, + serverResultId, + clientComponentId, + clientResultId } }) @@ -149,7 +183,7 @@ async function buildSetup (options) { await runWebpack(config) } -async function buildQueued (options, filePath, clearCache = false) { +async function buildQueued (options, filePath, data = {}, clearCache = false) { const queueId = getQueueId(options) if (clearCache) { @@ -167,7 +201,7 @@ async function buildQueued (options, filePath, clearCache = false) { } } - const promise = addFileToQueue(options, filePath, QUEUES[queueId]) + const promise = addFileToQueue(options, filePath, data, QUEUES[queueId]) debounce(queueId, async () => { const { config, handles } = QUEUES[queueId] @@ -178,25 +212,25 @@ async function buildQueued (options, filePath, clearCache = false) { const info = stats.toJson() const serverInfo = info.children.find(child => child.name === WEBPACK_NAME_SERVER) const clientInfo = info.children.find(child => child.name === WEBPACK_NAME_CLIENT) - const { ext, uiBase } = options - - Object.values(handles).forEach(({ resolve, filePath, serverId, clientId }) => { - const serverChunk = serverInfo && serverInfo.chunks.find(chunk => chunk.id === serverId) - const clientChunk = clientInfo && clientInfo.chunks.find(chunk => chunk.id === clientId) - const serverRenderPath = serverChunk && join(filesDir(options), `${ext}-server.js`) - const serverComponentPath = serverChunk && join(filesDir(options), `${getFileId(filePath, 'server')}.js`) - const clientRenderPath = clientChunk && `${uiBase}${TARGET_FOLDER}/${ext}-client.js` - const clientComponentPath = clientChunk && `${uiBase}${TARGET_FOLDER}/${getFileId(filePath, 'client')}.js` + const { uiBase } = options + + Object.values(handles).forEach(({ resolve, filePath, serverComponentId, clientComponentId }) => { + // needed for dependency resolution + const serverComponentChunk = serverInfo && serverInfo.chunks.find(chunk => chunk.id === serverComponentId) + const clientComponentChunk = clientInfo && clientInfo.chunks.find(chunk => chunk.id === clientComponentId) + // needed for property extraction + const serverComponentPath = serverComponentChunk && join(filesDir(options), `${getFileId(filePath, 'server-component')}.js`) + // needed for rendering + const serverResultPath = serverComponentChunk && join(filesDir(options), `${getFileId(filePath, 'server-result')}.js`) + const clientResultPath = clientComponentChunk && `${uiBase}${TARGET_FOLDER}/${getFileId(filePath, 'client-result')}.js` - // console.log(serverId, serverChunk, serverInfo.chunks) const object = { filePath, - serverChunk, - serverRenderPath, - serverComponentPath, - clientChunk, - clientRenderPath, - clientComponentPath + clientComponentChunk, + clientResultPath, + serverComponentChunk, + serverResultPath, + serverComponentPath } cachePut(queueId, filePath, object) diff --git a/packages/adapter-webpack/test/webpack_adapter_react_test.js b/packages/adapter-webpack/test/webpack_adapter_react_test.js index 51538a11..fd95f047 100644 --- a/packages/adapter-webpack/test/webpack_adapter_react_test.js +++ b/packages/adapter-webpack/test/webpack_adapter_react_test.js @@ -4,7 +4,7 @@ const assert = require('assert') const { join } = require('path') const { removeSync } = require('fs-extra') const { StringUtil: { crossPlatformPath } } = require('@uiengine/util') -const { assertMatches, assertIncludes, assertContentMatches } = require('../../../test/support/asserts') +const { assertMatches, assertIncludes, assertExists } = require('../../../test/support/asserts') const { testTmpPath } = require('../../../test/support/paths') const Adapter = require('../src/index') @@ -67,7 +67,7 @@ describe('Webpack adapter with React templates', function () { await Adapter.setup(options) const clientPath = join(outputPath, 'jsx-client.js') - assertContentMatches(clientPath, 'window["UIengineWebpack_render"]') + assertExists(clientPath) const serverPath = join(outputPath, 'jsx-server.js') const serverRender = require(serverPath) diff --git a/packages/adapter-webpack/test/webpack_adapter_vue_test.js b/packages/adapter-webpack/test/webpack_adapter_vue_test.js index 3568e998..c8c54d13 100644 --- a/packages/adapter-webpack/test/webpack_adapter_vue_test.js +++ b/packages/adapter-webpack/test/webpack_adapter_vue_test.js @@ -4,7 +4,7 @@ const assert = require('assert') const { join } = require('path') const { removeSync } = require('fs-extra') const { StringUtil: { crossPlatformPath } } = require('@uiengine/util') -const { assertMatches, assertIncludes, assertContentMatches } = require('../../../test/support/asserts') +const { assertMatches, assertIncludes, assertExists } = require('../../../test/support/asserts') const { testTmpPath } = require('../../../test/support/paths') const Adapter = require('../src/index') @@ -67,7 +67,7 @@ describe('Webpack adapter with Vue templates', function () { await Adapter.setup(options) const clientPath = join(outputPath, 'vue-client.js') - assertContentMatches(clientPath, 'window["UIengineWebpack_render"]') + assertExists(clientPath) const serverPath = join(outputPath, 'vue-server.js') const serverRender = require(serverPath) diff --git a/yarn.lock b/yarn.lock index c2bbc3f0..629588e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4786,7 +4786,7 @@ debug@3.1.0, debug@=3.1.0, debug@~3.1.0: dependencies: ms "2.0.0" -debug@3.2.6, debug@^3.1.0, debug@^3.2.6: +debug@3.2.6, debug@^3.0.0, debug@^3.1.0, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -14491,6 +14491,13 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1: source-list-map "^2.0.0" source-map "~0.6.1" +webpack-virtual-modules@^0.1.11: + version "0.1.11" + resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.1.11.tgz#8f82c74c0cc8d1ea05a0d5ed40fbe2b9b00a3ef5" + integrity sha512-cyaaKMkICVP333iPx+74f3azZ18qKWyvViLn0FnaU9xFki+jtdOfPlkxjQVKnSbE+0Hxz7GUEuDTU+boikWTvQ== + dependencies: + debug "^3.0.0" + webpack@4.39.2: version "4.39.2" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.39.2.tgz#c9aa5c1776d7c309d1b3911764f0288c8c2816aa"