mirror of
https://github.com/nuxt/nuxt.git
synced 2025-02-16 21:58:19 +00:00
feat: use webpack esm server build (#474)
This commit is contained in:
parent
7527d30bed
commit
193d7bf8bc
@ -7,6 +7,7 @@ import { getNitroContext, NitroContext } from './context'
|
|||||||
import { createDevServer } from './server/dev'
|
import { createDevServer } from './server/dev'
|
||||||
import { wpfs } from './utils/wpfs'
|
import { wpfs } from './utils/wpfs'
|
||||||
import { resolveMiddleware } from './server/middleware'
|
import { resolveMiddleware } from './server/middleware'
|
||||||
|
import AsyncLoadingPlugin from './webpack/wp4'
|
||||||
|
|
||||||
export default function nuxt2CompatModule (this: ModuleContainer) {
|
export default function nuxt2CompatModule (this: ModuleContainer) {
|
||||||
const { nuxt } = this
|
const { nuxt } = this
|
||||||
@ -64,6 +65,13 @@ export default function nuxt2CompatModule (this: ModuleContainer) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Set up webpack plugin for node async loading
|
||||||
|
nuxt.hook('webpack:config', (webpackConfigs) => {
|
||||||
|
const serverConfig = webpackConfigs.find(config => config.name === 'server')
|
||||||
|
serverConfig.plugins = serverConfig.plugins || []
|
||||||
|
serverConfig.plugins.push(new AsyncLoadingPlugin())
|
||||||
|
})
|
||||||
|
|
||||||
// Nitro client plugin
|
// Nitro client plugin
|
||||||
this.addPlugin({
|
this.addPlugin({
|
||||||
fileName: 'nitro.client.mjs',
|
fileName: 'nitro.client.mjs',
|
||||||
|
@ -161,14 +161,13 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
|||||||
rollupConfig.plugins.push(dynamicRequire({
|
rollupConfig.plugins.push(dynamicRequire({
|
||||||
dir: resolve(nitroContext._nuxt.buildDir, 'dist/server'),
|
dir: resolve(nitroContext._nuxt.buildDir, 'dist/server'),
|
||||||
inline: nitroContext.node === false || nitroContext.inlineDynamicImports,
|
inline: nitroContext.node === false || nitroContext.inlineDynamicImports,
|
||||||
globbyOptions: {
|
ignore: [
|
||||||
ignore: [
|
'client.manifest.mjs',
|
||||||
'client.manifest.mjs',
|
'server.js',
|
||||||
'server.cjs',
|
'server.cjs',
|
||||||
'server.mjs',
|
'server.mjs',
|
||||||
'server.manifest.mjs'
|
'server.manifest.mjs'
|
||||||
]
|
]
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Assets
|
// Assets
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { resolve } from 'upath'
|
import { resolve } from 'upath'
|
||||||
import globby, { GlobbyOptions } from 'globby'
|
import globby from 'globby'
|
||||||
import type { Plugin } from 'rollup'
|
import type { Plugin } from 'rollup'
|
||||||
|
|
||||||
const PLUGIN_NAME = 'dynamic-require'
|
const PLUGIN_NAME = 'dynamic-require'
|
||||||
const HELPER_DYNAMIC = `\0${PLUGIN_NAME}.js`
|
const HELPER_DYNAMIC = `\0${PLUGIN_NAME}.js`
|
||||||
const DYNAMIC_REQUIRE_RE = /require\("\.\/" ?\+/g
|
const DYNAMIC_REQUIRE_RE = /import\("\.\/" ?\+(.*)\).then/g
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
dir: string
|
dir: string
|
||||||
inline: boolean
|
inline: boolean
|
||||||
globbyOptions: GlobbyOptions
|
ignore: string[]
|
||||||
outDir?: string
|
outDir?: string
|
||||||
prefix?: string
|
prefix?: string
|
||||||
}
|
}
|
||||||
@ -29,19 +29,19 @@ interface TemplateContext {
|
|||||||
chunks: Chunk[]
|
chunks: Chunk[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dynamicRequire ({ dir, globbyOptions, inline }: Options): Plugin {
|
export function dynamicRequire ({ dir, ignore, inline }: Options): Plugin {
|
||||||
return {
|
return {
|
||||||
name: PLUGIN_NAME,
|
name: PLUGIN_NAME,
|
||||||
transform (code: string, _id: string) {
|
transform (code: string, _id: string) {
|
||||||
return {
|
return {
|
||||||
code: code.replace(DYNAMIC_REQUIRE_RE, `require('${HELPER_DYNAMIC}')(`),
|
code: code.replace(DYNAMIC_REQUIRE_RE, `import('${HELPER_DYNAMIC}').then(r => r.default || r).then(dynamicRequire => dynamicRequire($1)).then`),
|
||||||
map: null
|
map: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resolveId (id: string) {
|
resolveId (id: string) {
|
||||||
return id === HELPER_DYNAMIC ? id : null
|
return id === HELPER_DYNAMIC ? id : null
|
||||||
},
|
},
|
||||||
// TODO: Async chunk loading over netwrok!
|
// TODO: Async chunk loading over network!
|
||||||
// renderDynamicImport () {
|
// renderDynamicImport () {
|
||||||
// return {
|
// return {
|
||||||
// left: 'fetch(', right: ')'
|
// left: 'fetch(', right: ')'
|
||||||
@ -53,7 +53,13 @@ export function dynamicRequire ({ dir, globbyOptions, inline }: Options): Plugin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Scan chunks
|
// Scan chunks
|
||||||
const files = await globby('**/*.{cjs,mjs,js}', { cwd: dir, absolute: false, ...globbyOptions })
|
let files = []
|
||||||
|
try {
|
||||||
|
const wpManifest = resolve(dir, './server.manifest.json')
|
||||||
|
files = await import(wpManifest).then(r => Object.keys(r.files).filter(file => !ignore.includes(file)))
|
||||||
|
} catch {
|
||||||
|
files = await globby('**/*.{cjs,mjs,js}', { cwd: dir, absolute: false, ignore })
|
||||||
|
}
|
||||||
const chunks = files.map(id => ({
|
const chunks = files.map(id => ({
|
||||||
id,
|
id,
|
||||||
src: resolve(dir, id).replace(/\\/g, '/'),
|
src: resolve(dir, id).replace(/\\/g, '/'),
|
||||||
@ -62,20 +68,6 @@ export function dynamicRequire ({ dir, globbyOptions, inline }: Options): Plugin
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
return inline ? TMPL_INLINE({ chunks }) : TMPL_LAZY({ chunks })
|
return inline ? TMPL_INLINE({ chunks }) : TMPL_LAZY({ chunks })
|
||||||
},
|
|
||||||
renderChunk (code) {
|
|
||||||
if (inline) {
|
|
||||||
return {
|
|
||||||
map: null,
|
|
||||||
code
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
map: null,
|
|
||||||
code: code.replace(
|
|
||||||
/Promise.resolve\(\).then\(function \(\) \{ return require\('([^']*)' \/\* webpackChunk \*\/\); \}\).then\(function \(n\) \{ return n.([_a-zA-Z0-9]*); \}\)/g,
|
|
||||||
"require('$1').$2")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,47 +83,20 @@ function getWebpackChunkMeta (src: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function TMPL_INLINE ({ chunks }: TemplateContext) {
|
function TMPL_INLINE ({ chunks }: TemplateContext) {
|
||||||
return `${chunks.map(i => `import ${i.name} from '${i.src}'`).join('\n')}
|
return `${chunks.map(i => `import * as ${i.name} from '${i.src}'`).join('\n')}
|
||||||
const dynamicChunks = {
|
const dynamicChunks = {
|
||||||
${chunks.map(i => ` ['${i.id}']: ${i.name}`).join(',\n')}
|
${chunks.map(i => ` ['${i.id}']: ${i.name}`).join(',\n')}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function dynamicRequire(id) {
|
export default function dynamicRequire(id) {
|
||||||
return dynamicChunks[id];
|
return Promise.resolve(dynamicChunks[id]);
|
||||||
};`
|
};`
|
||||||
}
|
}
|
||||||
|
|
||||||
function TMPL_LAZY ({ chunks }: TemplateContext) {
|
function TMPL_LAZY ({ chunks }: TemplateContext) {
|
||||||
return `
|
return `
|
||||||
function dynamicWebpackModule(id, getChunk, ids) {
|
|
||||||
return function (module, exports, require) {
|
|
||||||
const r = getChunk()
|
|
||||||
if (typeof r.then === 'function') {
|
|
||||||
module.exports = r.then(r => {
|
|
||||||
const realModule = { exports: {}, require };
|
|
||||||
r.modules[id](realModule, realModule.exports, realModule.require);
|
|
||||||
for (const _id of ids) {
|
|
||||||
if (_id === id) continue;
|
|
||||||
r.modules[_id](realModule, realModule.exports, realModule.require);
|
|
||||||
}
|
|
||||||
return realModule.exports;
|
|
||||||
});
|
|
||||||
} else if (r && typeof r.modules[id] === 'function') {
|
|
||||||
r.modules[id](module, exports, require);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
function webpackChunk (meta, getChunk) {
|
|
||||||
const chunk = { ...meta, modules: {} };
|
|
||||||
for (const id of meta.moduleIds) {
|
|
||||||
chunk.modules[id] = dynamicWebpackModule(id, getChunk, meta.moduleIds);
|
|
||||||
};
|
|
||||||
return chunk;
|
|
||||||
};
|
|
||||||
|
|
||||||
const dynamicChunks = {
|
const dynamicChunks = {
|
||||||
${chunks.map(i => ` ['${i.id}']: () => webpackChunk(${JSON.stringify(i.meta)}, () => import('${i.src}' /* webpackChunk */))`).join(',\n')}
|
${chunks.map(i => ` ['${i.id}']: () => import('${i.src}')`).join(',\n')}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function dynamicRequire(id) {
|
export default function dynamicRequire(id) {
|
||||||
|
132
packages/nitro/src/webpack/wp4.ts
Normal file
132
packages/nitro/src/webpack/wp4.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// Based on https://github.com/webpack/webpack/blob/v4.46.0/lib/node/NodeMainTemplatePlugin.js#L81-L191
|
||||||
|
|
||||||
|
import { Compiler } from 'webpack'
|
||||||
|
import Template from 'webpack/lib/Template'
|
||||||
|
|
||||||
|
export default class AsyncLoadingPlugin {
|
||||||
|
apply (compiler: Compiler) {
|
||||||
|
compiler.hooks.compilation.tap('AsyncLoading', (compilation) => {
|
||||||
|
const mainTemplate = compilation.mainTemplate
|
||||||
|
mainTemplate.hooks.requireEnsure.tap(
|
||||||
|
'AsyncLoading',
|
||||||
|
(_source, chunk, hash) => {
|
||||||
|
const chunkFilename = mainTemplate.outputOptions.chunkFilename
|
||||||
|
const chunkMaps = chunk.getChunkMaps()
|
||||||
|
const insertMoreModules = [
|
||||||
|
'var moreModules = chunk.modules, chunkIds = chunk.ids;',
|
||||||
|
'for(var moduleId in moreModules) {',
|
||||||
|
Template.indent(
|
||||||
|
mainTemplate.renderAddModule(
|
||||||
|
hash,
|
||||||
|
chunk,
|
||||||
|
'moduleId',
|
||||||
|
'moreModules[moduleId]'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'}'
|
||||||
|
]
|
||||||
|
return Template.asString([
|
||||||
|
'// Async chunk loading for Nitro',
|
||||||
|
'',
|
||||||
|
'var installedChunkData = installedChunks[chunkId];',
|
||||||
|
'if(installedChunkData !== 0) { // 0 means "already installed".',
|
||||||
|
Template.indent([
|
||||||
|
'// array of [resolve, reject, promise] means "currently loading"',
|
||||||
|
'if(installedChunkData) {',
|
||||||
|
Template.indent(['promises.push(installedChunkData[2]);']),
|
||||||
|
'} else {',
|
||||||
|
Template.indent([
|
||||||
|
'// load the chunk and return promise to it',
|
||||||
|
'var promise = new Promise(function(resolve, reject) {',
|
||||||
|
Template.indent([
|
||||||
|
'installedChunkData = installedChunks[chunkId] = [resolve, reject];',
|
||||||
|
'import(' +
|
||||||
|
mainTemplate.getAssetPath(
|
||||||
|
JSON.stringify(`./${chunkFilename}`),
|
||||||
|
{
|
||||||
|
hash: `" + ${mainTemplate.renderCurrentHashCode(
|
||||||
|
hash
|
||||||
|
)} + "`,
|
||||||
|
hashWithLength: length =>
|
||||||
|
`" + ${mainTemplate.renderCurrentHashCode(
|
||||||
|
hash,
|
||||||
|
length
|
||||||
|
)} + "`,
|
||||||
|
chunk: {
|
||||||
|
id: '" + chunkId + "',
|
||||||
|
hash: `" + ${JSON.stringify(
|
||||||
|
chunkMaps.hash
|
||||||
|
)}[chunkId] + "`,
|
||||||
|
hashWithLength: (length) => {
|
||||||
|
const shortChunkHashMap = {}
|
||||||
|
for (const chunkId of Object.keys(chunkMaps.hash)) {
|
||||||
|
if (typeof chunkMaps.hash[chunkId] === 'string') {
|
||||||
|
shortChunkHashMap[chunkId] = chunkMaps.hash[
|
||||||
|
chunkId
|
||||||
|
].substr(0, length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `" + ${JSON.stringify(
|
||||||
|
shortChunkHashMap
|
||||||
|
)}[chunkId] + "`
|
||||||
|
},
|
||||||
|
contentHash: {
|
||||||
|
javascript: `" + ${JSON.stringify(
|
||||||
|
chunkMaps.contentHash.javascript
|
||||||
|
)}[chunkId] + "`
|
||||||
|
},
|
||||||
|
contentHashWithLength: {
|
||||||
|
javascript: (length) => {
|
||||||
|
const shortContentHashMap = {}
|
||||||
|
const contentHash =
|
||||||
|
chunkMaps.contentHash.javascript
|
||||||
|
for (const chunkId of Object.keys(contentHash)) {
|
||||||
|
if (typeof contentHash[chunkId] === 'string') {
|
||||||
|
shortContentHashMap[chunkId] = contentHash[
|
||||||
|
chunkId
|
||||||
|
].substr(0, length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `" + ${JSON.stringify(
|
||||||
|
shortContentHashMap
|
||||||
|
)}[chunkId] + "`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
name: `" + (${JSON.stringify(
|
||||||
|
chunkMaps.name
|
||||||
|
)}[chunkId]||chunkId) + "`
|
||||||
|
},
|
||||||
|
contentHashType: 'javascript'
|
||||||
|
}
|
||||||
|
) +
|
||||||
|
').then(chunk => {',
|
||||||
|
Template.indent(
|
||||||
|
insertMoreModules
|
||||||
|
.concat([
|
||||||
|
'var callbacks = [];',
|
||||||
|
'for(var i = 0; i < chunkIds.length; i++) {',
|
||||||
|
Template.indent([
|
||||||
|
'if(installedChunks[chunkIds[i]])',
|
||||||
|
Template.indent([
|
||||||
|
'callbacks = callbacks.concat(installedChunks[chunkIds[i]][0]);'
|
||||||
|
]),
|
||||||
|
'installedChunks[chunkIds[i]] = 0;'
|
||||||
|
]),
|
||||||
|
'}',
|
||||||
|
'for(i = 0; i < callbacks.length; i++)',
|
||||||
|
Template.indent('callbacks[i]();')
|
||||||
|
])
|
||||||
|
),
|
||||||
|
'});'
|
||||||
|
]),
|
||||||
|
'});',
|
||||||
|
'promises.push(installedChunkData[2] = promise);'
|
||||||
|
]),
|
||||||
|
'}'
|
||||||
|
]),
|
||||||
|
'}'
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,7 @@
|
|||||||
"vue": "3.2.2",
|
"vue": "3.2.2",
|
||||||
"vue-loader": "^16.5.0",
|
"vue-loader": "^16.5.0",
|
||||||
"vue-style-loader": "^4.1.3",
|
"vue-style-loader": "^4.1.3",
|
||||||
"webpack": "^5.50.0",
|
"webpack": "^5.52.0",
|
||||||
"webpack-bundle-analyzer": "^4.4.2",
|
"webpack-bundle-analyzer": "^4.4.2",
|
||||||
"webpack-dev-middleware": "^5.0.0",
|
"webpack-dev-middleware": "^5.0.0",
|
||||||
"webpack-hot-middleware": "^2.25.0",
|
"webpack-hot-middleware": "^2.25.0",
|
||||||
|
@ -22,7 +22,7 @@ export function server (ctx: WebpackConfigContext) {
|
|||||||
function serverPreset (ctx: WebpackConfigContext) {
|
function serverPreset (ctx: WebpackConfigContext) {
|
||||||
const { config } = ctx
|
const { config } = ctx
|
||||||
|
|
||||||
config.output.filename = 'server.cjs'
|
config.output.filename = 'server.mjs'
|
||||||
config.devtool = 'cheap-module-source-map'
|
config.devtool = 'cheap-module-source-map'
|
||||||
|
|
||||||
config.optimization = {
|
config.optimization = {
|
||||||
|
@ -81,14 +81,6 @@ export default class VueSSRServerPlugin {
|
|||||||
size: () => mjsSrc.length
|
size: () => mjsSrc.length
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Workaround for webpack
|
|
||||||
const serverJS = 'export { default } from "./server.cjs"'
|
|
||||||
assets['server.mjs'] = {
|
|
||||||
source: () => serverJS,
|
|
||||||
map: () => null,
|
|
||||||
size: () => serverJS.length
|
|
||||||
}
|
|
||||||
|
|
||||||
cb()
|
cb()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -10,11 +10,6 @@ export const validate = (compiler) => {
|
|||||||
consola.warn('webpack config `target` should be "node".')
|
consola.warn('webpack config `target` should be "node".')
|
||||||
}
|
}
|
||||||
|
|
||||||
const libraryType = compiler.options.output.library.type
|
|
||||||
if (libraryType !== 'commonjs2') {
|
|
||||||
consola.warn('webpack config `output.libraryTarget` should be "commonjs2".')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!compiler.options.externals) {
|
if (!compiler.options.externals) {
|
||||||
consola.info(
|
consola.info(
|
||||||
'It is recommended to externalize dependencies in the server build for ' +
|
'It is recommended to externalize dependencies in the server build for ' +
|
||||||
|
@ -31,6 +31,7 @@ function baseConfig (ctx: WebpackConfigContext) {
|
|||||||
...options.build.optimization as any,
|
...options.build.optimization as any,
|
||||||
minimizer: []
|
minimizer: []
|
||||||
},
|
},
|
||||||
|
experiments: {},
|
||||||
mode: ctx.isDev ? 'development' : 'production',
|
mode: ctx.isDev ? 'development' : 'production',
|
||||||
cache: getCache(ctx),
|
cache: getCache(ctx),
|
||||||
output: getOutput(ctx),
|
output: getOutput(ctx),
|
||||||
|
@ -7,7 +7,7 @@ export function esbuild (ctx: WebpackConfigContext) {
|
|||||||
// https://esbuild.github.io/getting-started/#bundling-for-the-browser
|
// https://esbuild.github.io/getting-started/#bundling-for-the-browser
|
||||||
// https://gs.statcounter.com/browser-version-market-share
|
// https://gs.statcounter.com/browser-version-market-share
|
||||||
// https://nodejs.org/en/
|
// https://nodejs.org/en/
|
||||||
const target = ctx.isServer ? 'node14' : 'chrome85'
|
const target = ctx.isServer ? 'es2019' : 'chrome85'
|
||||||
|
|
||||||
config.optimization.minimizer.push(new ESBuildMinifyPlugin())
|
config.optimization.minimizer.push(new ESBuildMinifyPlugin())
|
||||||
|
|
||||||
|
@ -6,13 +6,25 @@ export function node (ctx: WebpackConfigContext) {
|
|||||||
config.target = 'node'
|
config.target = 'node'
|
||||||
config.node = false
|
config.node = false
|
||||||
|
|
||||||
config.resolve.mainFields = ['main', 'module']
|
config.experiments.outputModule = true
|
||||||
|
|
||||||
config.output = {
|
config.output = {
|
||||||
...config.output,
|
...config.output,
|
||||||
chunkFilename: '[name].cjs',
|
chunkFilename: '[name].mjs',
|
||||||
|
chunkFormat: 'module',
|
||||||
|
chunkLoading: 'import',
|
||||||
|
module: true,
|
||||||
|
environment: {
|
||||||
|
module: true,
|
||||||
|
arrowFunction: true,
|
||||||
|
bigIntLiteral: true,
|
||||||
|
const: true,
|
||||||
|
destructuring: true,
|
||||||
|
dynamicImport: true,
|
||||||
|
forOf: true
|
||||||
|
},
|
||||||
library: {
|
library: {
|
||||||
type: 'commonjs2'
|
type: 'module'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
yarn.lock
10
yarn.lock
@ -1456,7 +1456,7 @@ __metadata:
|
|||||||
vue: 3.2.2
|
vue: 3.2.2
|
||||||
vue-loader: ^16.5.0
|
vue-loader: ^16.5.0
|
||||||
vue-style-loader: ^4.1.3
|
vue-style-loader: ^4.1.3
|
||||||
webpack: ^5.50.0
|
webpack: ^5.52.0
|
||||||
webpack-bundle-analyzer: ^4.4.2
|
webpack-bundle-analyzer: ^4.4.2
|
||||||
webpack-dev-middleware: ^5.0.0
|
webpack-dev-middleware: ^5.0.0
|
||||||
webpack-hot-middleware: ^2.25.0
|
webpack-hot-middleware: ^2.25.0
|
||||||
@ -12195,9 +12195,9 @@ typescript@^4.3.5:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"webpack@npm:^5, webpack@npm:^5.1.0, webpack@npm:^5.38.1, webpack@npm:^5.50.0":
|
"webpack@npm:^5, webpack@npm:^5.1.0, webpack@npm:^5.38.1, webpack@npm:^5.52.0":
|
||||||
version: 5.50.0
|
version: 5.52.0
|
||||||
resolution: "webpack@npm:5.50.0"
|
resolution: "webpack@npm:5.52.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/eslint-scope": ^3.7.0
|
"@types/eslint-scope": ^3.7.0
|
||||||
"@types/estree": ^0.0.50
|
"@types/estree": ^0.0.50
|
||||||
@ -12228,7 +12228,7 @@ typescript@^4.3.5:
|
|||||||
optional: true
|
optional: true
|
||||||
bin:
|
bin:
|
||||||
webpack: bin/webpack.js
|
webpack: bin/webpack.js
|
||||||
checksum: 293bed1d9101ac127605f35a225a5cbc1bc89eac68d6e09e7feb3e284ec2693b3db7c1dd7710fadf6852f89ad39ed09413c35befa1cfc9738074b33299ac2b9e
|
checksum: fe7cbb761b251a6885d67971f8763a9675ca4777ff863be4cbe76a6ab22a3f810be2728fe7b9c31f74259001859a3915ad581f0e4aca5255cdb13ccab3472f00
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user