Skip to content

Commit 417981c

Browse files
committed
feature #1142 Add support for pnpm and Yarn PnP (Kocal)
This PR was squashed before being merged into the main branch. Discussion ---------- Add support for pnpm and Yarn PnP For Yarn PnP and pnpm support, Encore needs to configure `peerDependenciesMeta` as explained in #655. I've configured **every** packages listed in `lib/features.js` as **optional** peerDependencies, and `webpack` as a **required** peer dependency. I'm not sure of how we can write tests for that, any idea `@weaverryan`? Maybe an E2E test with a real app/dedicated CI workflow? **EDIT:** implemented E2E tests for Yarn PnP and pnpm. Thanks! Commits ------- dbf3db4 Add support for pnpm and Yarn PnP
2 parents b1c50ce + dbf3db4 commit 417981c

27 files changed

+5028
-98
lines changed

.github/workflows/testing_apps.yml

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Testing apps
2+
on: [push, pull_request]
3+
4+
jobs:
5+
testing_app:
6+
strategy:
7+
fail-fast: false
8+
matrix:
9+
test_app:
10+
- name: npm
11+
working_directory: test_apps/npm
12+
script: |
13+
npm install --ci
14+
npm add --save-dev ../../webpack-encore.tgz
15+
npm run encore dev
16+
npm run encore production
17+
18+
- name: pnpm
19+
working_directory: test_apps/pnpm
20+
script: |
21+
pnpm install --frozen-lockfile
22+
pnpm add --save-dev ../../webpack-encore.tgz
23+
pnpm run encore dev
24+
pnpm run encore production
25+
26+
- name: Yarn Plug'n'Play
27+
working_directory: test_apps/yarn_pnp
28+
script: |
29+
yarn set version berry
30+
yarn install --frozen-lockfile
31+
yarn add --dev ../../webpack-encore.tgz
32+
yarn run encore dev
33+
yarn run encore production
34+
35+
name: ${{ matrix.test_app.name }}
36+
runs-on: ubuntu-latest
37+
38+
steps:
39+
- name: Checkout
40+
uses: actions/[email protected]
41+
42+
- name: Node ${{matrix.node-versions}}
43+
uses: actions/setup-node@v2
44+
with:
45+
node-version: '14'
46+
47+
- if: matrix.test_app.name == 'pnpm'
48+
name: Install pnpm
49+
uses: pnpm/action-setup@v2
50+
with:
51+
version: latest
52+
53+
- name: Packing Encore
54+
run: yarn pack --filename webpack-encore.tgz
55+
56+
- name: Running script
57+
working-directory: ${{ matrix.test_app.working_directory }}
58+
run: ${{ matrix.test_app.script }}

lib/WebpackConfig.js

+1-31
Original file line numberDiff line numberDiff line change
@@ -36,44 +36,14 @@ function validateRuntimeConfig(runtimeConfig) {
3636
}
3737
}
3838

39-
/**
40-
* @param {RuntimeConfig} runtimeConfig
41-
* @return {void}
42-
*/
43-
function checkPackageJson(runtimeConfig) {
44-
// Display a warning if webpack is listed as a [dev-]dependency
45-
try {
46-
const packageInfo = JSON.parse(
47-
fs.readFileSync(path.resolve(runtimeConfig.context, 'package.json'), { encoding: 'utf-8' })
48-
);
49-
50-
if (packageInfo) {
51-
const dependencies = new Set([
52-
...(packageInfo.dependencies ? Object.keys(packageInfo.dependencies) : []),
53-
...(packageInfo.devDependencies ? Object.keys(packageInfo.devDependencies) : []),
54-
]);
55-
56-
if (dependencies.has('webpack')) {
57-
logger.warning('Webpack is already provided by Webpack Encore, also adding it to your package.json file may cause issues.');
58-
}
59-
}
60-
} catch (e) {
61-
logger.warning('Could not read package.json file.');
62-
}
63-
}
64-
6539
class WebpackConfig {
66-
constructor(runtimeConfig, enablePackageJsonCheck = false) {
40+
constructor(runtimeConfig) {
6741
validateRuntimeConfig(runtimeConfig);
6842

6943
if (runtimeConfig.verbose) {
7044
logger.verbose();
7145
}
7246

73-
if (enablePackageJsonCheck) {
74-
checkPackageJson(runtimeConfig);
75-
}
76-
7747
this.runtimeConfig = runtimeConfig;
7848
this.entries = new Map();
7949
this.styleEntries = new Map();

lib/loaders/babel.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,22 @@ module.exports = {
5353

5454
Object.assign(babelConfig, {
5555
presets: [
56-
['@babel/preset-env', presetEnvOptions]
56+
[require.resolve('@babel/preset-env'), presetEnvOptions]
5757
],
58-
plugins: ['@babel/plugin-syntax-dynamic-import']
58+
plugins: [require.resolve('@babel/plugin-syntax-dynamic-import')]
5959
});
6060

6161
if (webpackConfig.useBabelTypeScriptPreset) {
6262
loaderFeatures.ensurePackagesExistAndAreCorrectVersion('typescript-babel');
6363

64-
babelConfig.presets.push(['@babel/preset-typescript', webpackConfig.babelTypeScriptPresetOptions]);
65-
babelConfig.plugins.push('@babel/plugin-proposal-class-properties');
64+
babelConfig.presets.push([require.resolve('@babel/preset-typescript'), webpackConfig.babelTypeScriptPresetOptions]);
65+
babelConfig.plugins.push(require.resolve('@babel/plugin-proposal-class-properties'));
6666
}
6767

6868
if (webpackConfig.useReact) {
6969
loaderFeatures.ensurePackagesExistAndAreCorrectVersion('react');
7070

71-
babelConfig.presets.push('@babel/react');
71+
babelConfig.presets.push(require.resolve('@babel/preset-react'));
7272
}
7373

7474
if (webpackConfig.usePreact) {
@@ -77,20 +77,20 @@ module.exports = {
7777
if (webpackConfig.preactOptions.preactCompat) {
7878
// If preact-compat is enabled tell babel to
7979
// transform JSX into React.createElement calls.
80-
babelConfig.plugins.push(['@babel/plugin-transform-react-jsx']);
80+
babelConfig.plugins.push([require.resolve('@babel/plugin-transform-react-jsx')]);
8181
} else {
8282
// If preact-compat is disabled tell babel to
8383
// transform JSX into Preact h() calls.
8484
babelConfig.plugins.push([
85-
'@babel/plugin-transform-react-jsx',
85+
require.resolve('@babel/plugin-transform-react-jsx'),
8686
{ 'pragma': 'h' }
8787
]);
8888
}
8989
}
9090

9191
if (webpackConfig.useVueLoader && webpackConfig.vueOptions.useJsx) {
9292
loaderFeatures.ensurePackagesExistAndAreCorrectVersion('vue-jsx');
93-
babelConfig.presets.push('@vue/babel-preset-jsx');
93+
babelConfig.presets.push(require.resolve('@vue/babel-preset-jsx'));
9494
}
9595

9696
babelConfig = applyOptionsCallback(webpackConfig.babelConfigurationCallback, babelConfig);

package.json

+127-2
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,9 @@
4545
"semver": "^7.3.2",
4646
"style-loader": "^3.3.0",
4747
"sync-rpc": "^1.3.6",
48+
"tapable": "^2.2.1",
4849
"terser-webpack-plugin": "^5.3.0",
4950
"tmp": "^0.2.1",
50-
"webpack": "^5.72",
51-
"webpack-cli": "^4.9.1",
5251
"webpack-dev-server": "^4.8.0",
5352
"yargs-parser": "^21.0.0"
5453
},
@@ -98,9 +97,135 @@
9897
"vue": "^3.2.14",
9998
"vue-loader": "^17.0.0",
10099
"vue-template-compiler": "^2.5",
100+
"webpack": "^5.72",
101+
"webpack-cli": "^4.9.1",
101102
"webpack-notifier": "^1.15.0",
102103
"zombie": "^6.1.4"
103104
},
105+
"peerDependencies": {
106+
"@babel/plugin-proposal-class-properties": "^7.0.0",
107+
"@babel/plugin-transform-react-jsx": "^7.12.11",
108+
"@babel/preset-react": "^7.0.0",
109+
"@babel/preset-typescript": "^7.0.0",
110+
"@symfony/stimulus-bridge": "^3.0.0",
111+
"@vue/babel-helper-vue-jsx-merge-props": "^1.0.0",
112+
"@vue/babel-preset-jsx": "^1.0.0",
113+
"@vue/compiler-sfc": "^3.0.2",
114+
"eslint": "^8.0.0",
115+
"eslint-webpack-plugin": "^3.1.0",
116+
"file-loader": "^6.0.0",
117+
"fork-ts-checker-webpack-plugin": "^7.0.0",
118+
"handlebars": "^4.7.7",
119+
"handlebars-loader": "^1.7.0",
120+
"less": "^4.0.0",
121+
"less-loader": "^11.0.0",
122+
"postcss": "^8.3.0",
123+
"postcss-loader": "^7.0.0",
124+
"sass": "^1.17.0",
125+
"sass-loader": "^13.0.0",
126+
"stylus": "^0.58.1",
127+
"stylus-loader": "^7.0.0",
128+
"ts-loader": "^9.0.0",
129+
"typescript": "^4.2.2",
130+
"vue": "^3.2.14",
131+
"vue-loader": "^17.0.0",
132+
"vue-template-compiler": "^2.5",
133+
"webpack": "^5.72",
134+
"webpack-cli": "^4.9.1",
135+
"webpack-notifier": "^1.15.0"
136+
},
137+
"peerDependenciesMeta": {
138+
"@babel/plugin-proposal-class-properties": {
139+
"optional": true
140+
},
141+
"@babel/plugin-transform-react-jsx": {
142+
"optional": true
143+
},
144+
"@babel/preset-react": {
145+
"optional": true
146+
},
147+
"@babel/preset-typescript": {
148+
"optional": true
149+
},
150+
"@symfony/stimulus-bridge": {
151+
"optional": true
152+
},
153+
"@vue/babel-helper-vue-jsx-merge-props": {
154+
"optional": true
155+
},
156+
"@vue/babel-preset-jsx": {
157+
"optional": true
158+
},
159+
"@vue/compiler-sfc": {
160+
"optional": true
161+
},
162+
"eslint": {
163+
"optional": true
164+
},
165+
"eslint-webpack-plugin": {
166+
"optional": true
167+
},
168+
"file-loader": {
169+
"optional": true
170+
},
171+
"fork-ts-checker-webpack-plugin": {
172+
"optional": true
173+
},
174+
"handlebars": {
175+
"optional": true
176+
},
177+
"handlebars-loader": {
178+
"optional": true
179+
},
180+
"less": {
181+
"optional": true
182+
},
183+
"less-loader": {
184+
"optional": true
185+
},
186+
"postcss": {
187+
"optional": true
188+
},
189+
"postcss-loader": {
190+
"optional": true
191+
},
192+
"sass": {
193+
"optional": true
194+
},
195+
"sass-loader": {
196+
"optional": true
197+
},
198+
"stylus": {
199+
"optional": true
200+
},
201+
"stylus-loader": {
202+
"optional": true
203+
},
204+
"ts-loader": {
205+
"optional": true
206+
},
207+
"typescript": {
208+
"optional": true
209+
},
210+
"vue": {
211+
"optional": true
212+
},
213+
"vue-loader": {
214+
"optional": true
215+
},
216+
"vue-template-compiler": {
217+
"optional": true
218+
},
219+
"webpack": {
220+
"optional": false
221+
},
222+
"webpack-cli": {
223+
"optional": false
224+
},
225+
"webpack-notifier": {
226+
"optional": true
227+
}
228+
},
104229
"files": [
105230
"lib/",
106231
"bin/",

test/bin/encore.js

-42
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ module.exports = Encore.getWebpackConfig();
5656
}
5757

5858
expect(stdout).to.contain('Compiled successfully');
59-
expect(stdout).not.to.contain('Webpack is already provided by Webpack Encore');
6059

6160
expect(stdout).not.to.contain('Hash: ');
6261
expect(stdout).not.to.contain('Version: ');
@@ -176,47 +175,6 @@ module.exports = Encore.getWebpackConfig();
176175
});
177176
});
178177

179-
it('Display a warning message when webpack is also added to the package.json file', (done) => {
180-
testSetup.emptyTmpDir();
181-
const testDir = testSetup.createTestAppDir();
182-
183-
fs.writeFileSync(
184-
path.join(testDir, 'package.json'),
185-
`{
186-
"devDependencies": {
187-
"@symfony/webpack-encore": "*",
188-
"webpack": "*"
189-
}
190-
}`
191-
);
192-
193-
fs.writeFileSync(
194-
path.join(testDir, 'webpack.config.js'),
195-
`
196-
const Encore = require('../../index.js');
197-
Encore
198-
.enableSingleRuntimeChunk()
199-
.setOutputPath('build/')
200-
.setPublicPath('/build')
201-
.addEntry('main', './js/no_require')
202-
;
203-
204-
module.exports = Encore.getWebpackConfig();
205-
`
206-
);
207-
208-
const binPath = path.resolve(__dirname, '../', '../', 'bin', 'encore.js');
209-
exec(`node ${binPath} dev --context=${testDir}`, { cwd: testDir }, (err, stdout, stderr) => {
210-
if (err) {
211-
throw new Error(`Error executing encore: ${err} ${stderr} ${stdout}`);
212-
}
213-
214-
expect(stdout).to.contain('Webpack is already provided by Webpack Encore');
215-
216-
done();
217-
});
218-
});
219-
220178
it('Display an error when calling an unknown method', (done) => {
221179
testSetup.emptyTmpDir();
222180
const testDir = testSetup.createTestAppDir();

0 commit comments

Comments
 (0)