Skip to content

Commit 4b81641

Browse files
authored
feat(cli): add monorepo and Yarn PnP support (#1418)
1 parent cc2f3e7 commit 4b81641

11 files changed

Lines changed: 160 additions & 68 deletions

File tree

.changeset/wicked-baboons-visit.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'preact-cli': minor
3+
---
4+
5+
Added monorepo and Yarn PnP support by correctly loading dependencies, removing faulty install checks, and adding undeclared dependencies

.github/workflows/ci.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,36 @@ jobs:
4444
LIGHTHOUSE_CHROMIUM_PATH: 'which google-chrome-stable'
4545
run: npm run test
4646

47+
pnpTest:
48+
name: PnPTest
49+
runs-on: ubuntu-latest
50+
steps:
51+
- uses: actions/checkout@v2
52+
with:
53+
fetch-depth: 1
54+
55+
- uses: actions/setup-node@v1
56+
with:
57+
node-version: 12.x
58+
59+
- name: Run PnP test
60+
run: |
61+
git clone https://github.com/preactjs-templates/default.git default
62+
cd default/template
63+
touch yarn.lock
64+
echo $(cat package.json | jq '.name = "pnp-test"') > package.json
65+
yarn set version 2
66+
yarn config set pnpFallbackMode none
67+
yarn config set compressionLevel 0
68+
yarn link -A -p ../..
69+
yarn build
70+
4771
ci-success:
4872
name: ci
4973
if: ${{ success() }}
5074
needs:
5175
- test
76+
- pnpTest
5277
runs-on: ubuntu-latest
5378
steps:
5479
- name: CI succeeded

packages/cli/lib/commands/build.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const rimraf = require('rimraf');
22
const { resolve } = require('path');
33
const { promisify } = require('util');
4-
const { isDir, error } = require('../util');
54
const runWebpack = require('../lib/webpack/run-webpack');
65
const { validateArgs } = require('./validate-args');
76

@@ -94,15 +93,6 @@ async function command(src, argv) {
9493
argv.production = toBool(argv.production);
9594

9695
let cwd = resolve(argv.cwd);
97-
let modules = resolve(cwd, 'node_modules');
98-
99-
if (!isDir(modules)) {
100-
return error(
101-
'No `node_modules` found! Please run `npm install` before continuing.',
102-
1
103-
);
104-
}
105-
10696
if (argv.clean === void 0) {
10797
let dest = resolve(cwd, argv.dest);
10898
await promisify(rimraf)(dest);

packages/cli/lib/lib/webpack/prerender.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,10 @@ module.exports = function (env, params) {
2626
return '';
2727
}
2828
const { cwd } = env;
29-
30-
const preact = require(require.resolve(`${cwd}/node_modules/preact`));
31-
const renderToString = require(require.resolve(
32-
`${cwd}/node_modules/preact-render-to-string`
33-
));
29+
const preact = require(require.resolve('preact', { paths: [cwd] }));
30+
const renderToString = require(require.resolve('preact-render-to-string', {
31+
paths: [cwd],
32+
}));
3433
return renderToString(preact.h(app, { ...params, url }));
3534
} catch (err) {
3635
let stack = stackTrace.parse(err).filter(s => s.getFileName() === entry)[0];

packages/cli/lib/lib/webpack/proxy-loader.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
var utils = require('loader-utils');
2-
var requireRelative = require('require-relative');
32

43
function proxyLoader(source, map) {
54
var options = utils.getOptions(this);
@@ -17,7 +16,9 @@ function proxyLoader(source, map) {
1716

1817
var loader;
1918
try {
20-
loader = requireRelative(proxyOptions.loader, proxyOptions.cwd);
19+
loader = require(require.resolve(proxyOptions.loader, {
20+
paths: [proxyOptions.cwd],
21+
}));
2122
} catch (e) {
2223
loader = require(proxyOptions.loader);
2324
}

packages/cli/lib/lib/webpack/render-html-plugin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ module.exports = async function (config) {
6363
: resolve(dest, url.substring(1), 'index.html');
6464
return Object.assign(values, {
6565
filename,
66-
template: `!!ejs-loader?esModule=false!${template}`,
66+
template: `!!${require.resolve('ejs-loader')}?esModule=false!${template}`,
6767
minify: isProd && {
6868
collapseWhitespace: true,
6969
removeScriptTypeAttributes: true,

packages/cli/lib/lib/webpack/transform-config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,10 @@ class WebpackConfigHelpers {
288288
} catch (e) {}
289289

290290
let templatePath = isPath
291-
? `!!ejs-loader?esModule=false!${resolve(this._cwd, template)}`
291+
? `!!${require.resolve('ejs-loader')}?esModule=false!${resolve(
292+
this._cwd,
293+
template
294+
)}`
292295
: template;
293296
let { plugin: htmlWebpackPlugin } = this.getPluginsByName(
294297
config,

packages/cli/lib/lib/webpack/utils.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
const resolveFrom = require('resolve-from');
2-
31
function isInstalledVersionPreactXOrAbove(cwd) {
42
try {
53
return (
6-
parseInt(require(resolveFrom(cwd, 'preact/package.json')).version, 10) >=
7-
10
4+
parseInt(
5+
require(require.resolve('preact/package.json', { paths: [cwd] }))
6+
.version,
7+
10
8+
) >= 10
89
);
910
} catch (e) {}
1011
return false;

packages/cli/lib/lib/webpack/webpack-base-config.js

Lines changed: 58 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
const webpack = require('webpack');
22
const path = require('path');
3-
const { resolve } = require('path');
3+
const { resolve, dirname } = require('path');
44
const { readFileSync, existsSync } = require('fs');
55
const { isInstalledVersionPreactXOrAbove } = require('./utils');
66
const autoprefixer = require('autoprefixer');
77
const browserslist = require('browserslist');
8-
const requireRelative = require('require-relative');
98
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
109
const FixStyleOnlyEntriesPlugin = require('webpack-fix-style-only-entries');
1110
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
1211
const ReplacePlugin = require('webpack-plugin-replace');
1312
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
1413
const createBabelConfig = require('../babel-config');
1514
const loadPostcssConfig = require('postcss-load-config');
15+
const PnpWebpackPlugin = require(`pnp-webpack-plugin`);
1616

1717
function readJson(file) {
1818
try {
@@ -96,9 +96,16 @@ module.exports = function (env) {
9696

9797
let compat = 'preact-compat';
9898
try {
99-
requireRelative.resolve('preact/compat', cwd);
100-
compat = 'preact/compat';
101-
} catch (e) {}
99+
compat = dirname(
100+
require.resolve('preact/compat/package.json', { paths: [cwd] })
101+
);
102+
} catch (e) {
103+
try {
104+
compat = dirname(
105+
require.resolve('preact-compat/package.json', { paths: [cwd] })
106+
);
107+
} catch (e) {}
108+
}
102109

103110
let tsconfig = resolveTsconfig(cwd, isProd);
104111

@@ -110,6 +117,14 @@ module.exports = function (env) {
110117
postcssPlugins = [autoprefixer({ overrideBrowserslist: browsers })];
111118
}
112119

120+
function tryResolveOptionalLoader(name) {
121+
try {
122+
return require.resolve(name);
123+
} catch (e) {
124+
return name;
125+
}
126+
}
127+
113128
return {
114129
context: src,
115130

@@ -130,21 +145,23 @@ module.exports = function (env) {
130145
'.css',
131146
'.wasm',
132147
],
133-
alias: Object.assign(
134-
{
135-
style: source('style'),
136-
'preact-cli-entrypoint': source('index'),
137-
url: 'native-url',
138-
// preact-compat aliases for supporting React dependencies:
139-
react: compat,
140-
'react-dom': compat,
141-
'react-addons-css-transition-group': 'preact-css-transition-group',
142-
'preact-cli/async-component': IS_SOURCE_PREACT_X_OR_ABOVE
143-
? require.resolve('@preact/async-loader/async')
144-
: require.resolve('@preact/async-loader/async-legacy'),
145-
},
146-
compat !== 'preact-compat' ? { 'preact-compat': compat } : {}
147-
),
148+
alias: {
149+
style: source('style'),
150+
'preact-cli-entrypoint': source('index'),
151+
url: dirname(require.resolve('native-url/package.json')),
152+
// preact-compat aliases for supporting React dependencies:
153+
react: compat,
154+
'react-dom': compat,
155+
'preact-compat': compat,
156+
'react-addons-css-transition-group': 'preact-css-transition-group',
157+
'preact-cli/async-component': IS_SOURCE_PREACT_X_OR_ABOVE
158+
? require.resolve('@preact/async-loader/async')
159+
: require.resolve('@preact/async-loader/async-legacy'),
160+
},
161+
plugins: [
162+
// TODO: Remove when upgrading to webpack 5
163+
PnpWebpackPlugin,
164+
],
148165
},
149166

150167
resolveLoader: {
@@ -162,7 +179,7 @@ module.exports = function (env) {
162179
test: /\.m?[jt]sx?$/,
163180
resolve: { mainFields: ['module', 'jsnext:main', 'browser', 'main'] },
164181
type: 'javascript/auto',
165-
loader: 'babel-loader',
182+
loader: require.resolve('babel-loader'),
166183
options: Object.assign(
167184
{ babelrc: false },
168185
createBabelConfig(env, { browsers }),
@@ -175,10 +192,10 @@ module.exports = function (env) {
175192
test: /\.less$/,
176193
use: [
177194
{
178-
loader: 'proxy-loader',
195+
loader: require.resolve('./proxy-loader'),
179196
options: {
180197
cwd,
181-
loader: 'less-loader',
198+
loader: tryResolveOptionalLoader('less-loader'),
182199
options: {
183200
sourceMap: true,
184201
lessOptions: {
@@ -195,10 +212,10 @@ module.exports = function (env) {
195212
test: /\.s[ac]ss$/,
196213
use: [
197214
{
198-
loader: 'proxy-loader',
215+
loader: require.resolve('./proxy-loader'),
199216
options: {
200217
cwd,
201-
loader: 'sass-loader',
218+
loader: tryResolveOptionalLoader('sass-loader'),
202219
options: getSassConfiguration(...nodeModules),
203220
},
204221
},
@@ -210,10 +227,10 @@ module.exports = function (env) {
210227
test: /\.styl$/,
211228
use: [
212229
{
213-
loader: 'proxy-loader',
230+
loader: require.resolve('./proxy-loader'),
214231
options: {
215232
cwd,
216-
loader: 'stylus-loader',
233+
loader: tryResolveOptionalLoader('stylus-loader'),
217234
options: {
218235
sourceMap: true,
219236
paths: nodeModules,
@@ -227,9 +244,11 @@ module.exports = function (env) {
227244
test: /\.(p?css|less|s[ac]ss|styl)$/,
228245
include: [source('components'), source('routes')],
229246
use: [
230-
isWatch ? 'style-loader' : MiniCssExtractPlugin.loader,
247+
isWatch
248+
? require.resolve('style-loader')
249+
: MiniCssExtractPlugin.loader,
231250
{
232-
loader: 'css-loader',
251+
loader: require.resolve('css-loader'),
233252
options: {
234253
modules: {
235254
localIdentName: '[local]__[hash:base64:5]',
@@ -239,7 +258,7 @@ module.exports = function (env) {
239258
},
240259
},
241260
{
242-
loader: 'postcss-loader',
261+
loader: require.resolve('postcss-loader'),
243262
options: {
244263
ident: 'postcss',
245264
sourceMap: true,
@@ -253,15 +272,17 @@ module.exports = function (env) {
253272
test: /\.(p?css|less|s[ac]ss|styl)$/,
254273
exclude: [source('components'), source('routes')],
255274
use: [
256-
isWatch ? 'style-loader' : MiniCssExtractPlugin.loader,
275+
isWatch
276+
? require.resolve('style-loader')
277+
: MiniCssExtractPlugin.loader,
257278
{
258-
loader: 'css-loader',
279+
loader: require.resolve('css-loader'),
259280
options: {
260281
sourceMap: true,
261282
},
262283
},
263284
{
264-
loader: 'postcss-loader',
285+
loader: require.resolve('postcss-loader'),
265286
options: {
266287
ident: 'postcss',
267288
sourceMap: true,
@@ -277,11 +298,13 @@ module.exports = function (env) {
277298
},
278299
{
279300
test: /\.(xml|html|txt|md)$/,
280-
loader: 'raw-loader',
301+
loader: require.resolve('raw-loader'),
281302
},
282303
{
283304
test: /\.(svg|woff2?|ttf|eot|jpe?g|png|webp|gif|mp4|mov|ogg|webm)(\?.*)?$/i,
284-
loader: isProd ? 'file-loader' : 'url-loader',
305+
loader: isProd
306+
? require.resolve('file-loader')
307+
: require.resolve('url-loader'),
285308
},
286309
],
287310
},

packages/cli/package.json

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"devDependencies": {
4848
"html-looks-like": "^1.0.2",
4949
"jest": "^26.0.1",
50+
"less-loader": "^7.0.1",
5051
"ncp": "^2.0.0",
5152
"node-sass": "^4.12.0",
5253
"p-retry": "^4.1.0",
@@ -57,11 +58,26 @@
5758
"puppeteer": "^5.3.1",
5859
"sass-loader": "^10.0.4",
5960
"shelljs": "^0.8.3",
60-
"sirv": "^1.0.0-next.2"
61+
"sirv": "^1.0.0-next.2",
62+
"stylus-loader": "^3.0.2"
6163
},
6264
"peerDependencies": {
65+
"less-loader": "^7.0.1",
6366
"preact": "*",
64-
"preact-render-to-string": "*"
67+
"preact-render-to-string": "*",
68+
"sass-loader": "^10.0.0 || ^9.0.2",
69+
"stylus-loader": "^3.0.2"
70+
},
71+
"peerDependenciesMeta": {
72+
"less-loader": {
73+
"optional": true
74+
},
75+
"sass-loader": {
76+
"optional": true
77+
},
78+
"stylus-loader": {
79+
"optional": true
80+
}
6581
},
6682
"dependencies": {
6783
"@babel/core": "^7.9.0",
@@ -107,15 +123,14 @@
107123
"native-url": "0.3.4",
108124
"optimize-css-assets-webpack-plugin": "^5.0.1",
109125
"ora": "^4.0.3",
126+
"pnp-webpack-plugin": "^1.6.4",
110127
"postcss-load-config": "^2.1.0",
111128
"postcss-loader": "^3.0.0",
112129
"progress-bar-webpack-plugin": "^2.1.0",
113130
"promise-polyfill": "^8.1.0",
114131
"prompts": "^2.2.1",
115132
"raw-loader": "^4.0.0",
116133
"react-refresh": "0.8.3",
117-
"require-relative": "^0.8.7",
118-
"resolve-from": "^5.0.0",
119134
"rimraf": "^3.0.2",
120135
"sade": "^1.4.1",
121136
"size-plugin": "^2.0.1",

0 commit comments

Comments
 (0)