Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wip): add filesystem cache config for webpack 5 #6323

Draft
wants to merge 8 commits into
base: dev
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -269,3 +269,5 @@ test(`should use formatter 'codeframe'`, async () => {

await donePromise
})

test.todo('should not skip lint when `vue-cli-service serve` hits the filesystem cache')
61 changes: 61 additions & 0 deletions packages/@vue/cli-service/__tests__/cache.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
jest.setTimeout(30000)

const path = require('path')

const { defaultPreset } = require('@vue/cli/lib/options')
const create = require('@vue/cli-test-utils/createTestProject')
// const launchPuppeteer = require('@vue/cli-test-utils/launchPuppeteer')

const { hashElement } = require('folder-hash')

test('should build with cache on two consecutive runs', async () => {
const project = await create('cache-basic', defaultPreset)
const cacheDir = path.join(project.dir, './node_modules/.cache')

await project.run('vue-cli-service build')
const cacheHash = await hashElement(cacheDir)

await project.run('vue-cli-service build')
const newCacheHash = await hashElement(cacheDir)

expect(newCacheHash).toEqual(cacheHash)

// TODO: the mtime of the files in each of the sub directories should not change
})

test.todo('should ignore serve cache when `--no-cache` argument is passed')

test.todo('should ignore build cache when `--no-cache` argument is passed')

test.todo('should ignore test cache when `--no-cache` argument is passed')

test('should ignore previous cache when `--mode` argument changed', async () => {
const project = await create('cache-mode', defaultPreset)
const cacheDir = path.join(project.dir, './node_modules/.cache')

await project.run('vue-cli-service build')
const cacheHash = await hashElement(cacheDir)

await project.run('vue-cli-service build --mode development')
const newCacheHash = await hashElement(cacheDir)

expect(newCacheHash).not.toEqual(cacheHash)
})

test.todo('should ignore previous cache when corresponding `.env` file changed')

test.todo('should not ignore previous cache when irrelevant `.env` file changed')

test.todo('should ignore previous cache when `--target` argument changed')

test.todo('should ignore previous cache when `--module` flag is changed')

test.todo('should ignore previous cache when `package.json` changed')

test.todo('should ignore previous cache when lockfiles changed')

test.todo('should ignore previous cache when Vue CLI config file changed')

test.todo('should ignore previous cache when babel config changed')

test.todo('should ignore previous cache when tsconfig.json changed')
29 changes: 29 additions & 0 deletions packages/@vue/cli-service/lib/PluginAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,35 @@ class PluginAPI {
const cacheIdentifier = hash(variables)
return { cacheDirectory, cacheIdentifier }
}

/**
* Vue CLI enables the filesystem cache in webpack 5 by default.
* If your plugin depends on an additional config file (e.g. `stylelint.config.js`),
* the default cache config does not take that file into account,
* thus causing unexpected cache persistence.
*
* To avoid such problems, you should add that file to the `buildDependencies`
* of webpack by calling this function.
* (e.g. `api.addBuildDependencies('stylelint.config.js')`)
*
* Please be aware that these dependencies are not evaluated.
* So if you depend on some external environment variables, please
* use the `appendCacheIdentifier` API to explicitly monitor their changes.
*
* @param {string[]|Object} deps if an array of strings is passed, it will be
* appended to the cache.buildDependencies.config field; if an object is passed,
* it will be merged into the cache.buildDependencies filed
*/
addBuildDependencies (deps) {
if (Array.isArray(deps)) {
this.configureWebpack(() => ({ cache: { buildDependencies: { config: deps } } }))
} else {
this.configureWebpack(() => ({ cache: { buildDependencies: deps } }))
}
}

// FIXME: Deal with random environment variables in `.js` buildDependencies
appendCacheIdentifier () {}
}

module.exports = PluginAPI
18 changes: 17 additions & 1 deletion packages/@vue/cli-service/lib/config/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,28 @@ module.exports = (api, options) => {
const isLegacyBundle = process.env.VUE_CLI_MODERN_MODE && !process.env.VUE_CLI_MODERN_BUILD
const resolveLocal = require('../util/resolveLocal')

// https://github.com/webpack/webpack/issues/11467#issuecomment-691873586
if (webpackMajor !== 4) {
// https://github.com/webpack/webpack/issues/11467#issuecomment-691873586
webpackConfig.module
.rule('esm')
.test(/\.m?jsx?$/)
.resolve.set('fullySpecified', false)

// Filesystem cache only available for webpack 5
webpackConfig.cache({
type: 'filesystem',
// Set different cache name for different modes and targets (and commands).
// Since we use environment variables for all kinds of purposes,
// the most straightforward way is to just find and combine those
// Vue CLI-specific environment variables.
name: Object.entries(process.env)
.filter(([key]) => key.startsWith('VUE_CLI') || key === 'NODE_ENV')
.map(([key, value]) => `${key}-${value}`)
.join(';'),
buildDependencies: {
config: [require.resolve('../../webpack.config')]
}
})
}

webpackConfig
Expand Down
6 changes: 6 additions & 0 deletions packages/@vue/cli-service/lib/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ const schema = createSchema(joi => joi.object({
})
}),

// for cache invalidation
buildDependencies: joi.alternatives().try(
joi.array().items(joi.string().required()),
joi.object()
),

// webpack
chainWebpack: joi.func(),
configureWebpack: joi.alternatives().try(
Expand Down
2 changes: 2 additions & 0 deletions packages/@vue/cli-service/lib/util/resolveUserConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ module.exports = function resolveUserConfig ({
} else {
resolved = inlineOptions || {}
resolvedFrom = 'inline options'

// FIXME: may need to call `api.appendCacheIdentifier` with inline options hash
}

// normalize some options
Expand Down
1 change: 1 addition & 0 deletions packages/@vue/cli-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
},
"devDependencies": {
"fibers": ">= 3.1.1 <6.0.0",
"folder-hash": "^4.0.1",
"sass": "^1.32.7",
"sass-loader": "^11.0.1",
"stylus-loader": "^5.0.0",
Expand Down
6 changes: 6 additions & 0 deletions packages/@vue/cli-service/types/ProjectOptions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@ interface ProjectOptions {

css?: CSSOptions;

/**
* Tell webpack to invalidate the build cache when changes are detected in these additional buildDependencies.
* Can be an array of config file paths or an object that is accepted by the webpack `cache.buildDependencies` option.
*/
buildDependencies?: Array<string> | Object,

/**
* A function that will receive an instance of `ChainableConfig` powered by [webpack-chain](https://github.com/mozilla-neutrino/webpack-chain)
*/
Expand Down
13 changes: 11 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10834,6 +10834,15 @@ focus-visible@^5.2.0:
resolved "https://registry.yarnpkg.com/focus-visible/-/focus-visible-5.2.0.tgz#3a9e41fccf587bd25dcc2ef045508284f0a4d6b3"
integrity sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ==

folder-hash@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/folder-hash/-/folder-hash-4.0.1.tgz#1603c46bdb899843e292ecdfca9e540dd22e1236"
integrity sha512-oF1MGtGAPezYJJRMRPzTwtDYwZdQ16UTnthsVAxjVZnlrQ36WuF6YxSgyZxnoUEK6JNPX+04FCFAkw5CzE5OMw==
dependencies:
debug "^4.1.1"
graceful-fs "~4.2.0"
minimatch "~3.0.4"

follow-redirects@^1.0.0, follow-redirects@^1.10.0:
version "1.14.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.0.tgz#f5d260f95c5f8c105894491feee5dc8993b402fe"
Expand Down Expand Up @@ -11654,7 +11663,7 @@ [email protected]:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==

graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4:
graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@~4.2.0:
version "4.2.6"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
Expand Down Expand Up @@ -15474,7 +15483,7 @@ minimalistic-crypto-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=

"minimatch@2 || 3", [email protected], minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2:
"minimatch@2 || 3", [email protected], minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2, minimatch@~3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
Expand Down