Skip to content

Commit 21964c1

Browse files
authored
add css/scss modules support, back (#1174)
* add css/scss modules support, back * add postcss back to dev client * upgrade ci node * appease linter * new node openssl override * doc fix * mise var; yarnrc for integrity issues * support node externals allowlist * alpha publishes * alpha publishes * support webpackhot external on dev server * bundle all css * dev srcmap * update to faster source map * custom externals algorithm * custom externals algorithm * remove externals dep * externals handle no deps in package.json * externals handle bad package.json resolution in e2e tests * webpack 4 bs
1 parent c562ab9 commit 21964c1

31 files changed

+1463
-184
lines changed

.github/workflows/continuous-integration.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
strategy:
1616
matrix:
17-
node-version: [12.x, 14.x, 16.x]
17+
node-version: [18.x, 20.x]
1818

1919
steps:
2020
- uses: actions/checkout@v2

.mise.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[tools]
2+
node = '20.10.0'
3+
# stuck on this version of yarn because of some weird problems
4+
# related to babel tooling when installing new dependencies
5+
yarn = '1.19.0'
6+
7+
[env]
8+
MISE_FETCH_REMOTE_VERSIONS_TIMEOUT="300 s"
9+
NODE_OPTIONS = "--openssl-legacy-provider"

.nvmrc

Lines changed: 0 additions & 1 deletion
This file was deleted.

.yarnrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
unsafe-disable-integrity-migration false

CONTRIBUTING.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,21 @@ Please make sure all PRs are:
1818

1919
If you want to propose a large feature idea or architecture change you should consider submitting an RFC. It's often helpful to get feedback on your concept in an issue before starting the RFC. RFCs are an evolving process in the `kyt` repository so expect a lot of changes and guidelines in the future. You can find the `kyt` RFC template [here](/rfc/template.md).
2020

21-
## kyt local development
21+
## kyt local development workflow
2222

23-
1. `nvm use`
2423
1. Fork and clone `kyt`
24+
1. [setup [mise](https://mise.jdx.dev/) and `mise install`]
2525
1. Run `yarn bootstrap` to install the packages in the monorepo
26+
1. Open a new shell and run `yarn watch`
2627

27-
[lerna](https://github.com/lerna/lerna) is used to manage the monorepo but most of the development commands should be exercised through root directory `package.json` scripts. The following are some useful npm scripts for development:
28+
Most changes are best to develop against the universal starter kyt:
29+
30+
1. `cd packages/kyt-starter-universal/starter-src`
31+
1. Run `yarn dev` or `yarn build` to test against kyt development and production builds
32+
33+
Note: After you make changes, the watcher will update libraries but you will likely have to restart the universal app process to test changes. The watcher only works against kyt-core, server and runtime. Changes to babel presets and a few other packages may require you to re-`yarn bootstrap` or `yarn clean-bootstrap`. When in doubt run `yarn clean-bootstrap`.
34+
35+
[lerna](https://github.com/lerna/lerna) is used to manage the monorepo but most of the development commands should be exercised through root directory `package.json` scripts.
2836

2937
### bootstrap
3038

@@ -77,14 +85,12 @@ pushing directly to `main`.
7785
For more information on using `lerna` to publish, see [the `lerna publish`
7886
documentation](https://github.com/lerna/lerna/tree/main/commands/publish#readme).
7987

80-
### Development Versions
88+
### Publishing Alpha Versions
8189

82-
If you would like your prerelease to have the `next` dist tag, rather than
83-
`latest`, such as when creating a release candidate or testing a development
84-
version, you can use the provided `publish:next` script.
90+
If you would like to publish alpha release versions, for example `[email protected]`:
8591

8692
```sh
87-
$ GH_TOKEN=$GITHUB_TOKEN npm run publish:next
93+
$ GH_TOKEN=$GITHUB_TOKEN npm run publish:alpha
8894
```
8995

9096
If you need more functionality than this, it is recommended that you pass your

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
# kyt
44

5+
### Deprecation notice
6+
7+
This project is still used internally at the new york times but has long been deprecated. Please don't expect any support or documented releases going forward.
8+
9+
---
10+
11+
---
12+
513
Every sizable JavaScript web app needs a common foundation: a setup to build, run, test and lint your code. `kyt` is a toolkit that encapsulates and manages the configuration for web apps.
614

715
Read more about kyt in our [blog post](https://open.nytimes.com/introducing-kyt-our-web-app-configuration-toolkit-9ccddf6f6988).

e2e_tests/tests/cli.test.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@ describe('KYT CLI', () => {
2626
it(`sets up a ${slug} starter-kyt`, () => {
2727
const exec = new Promise(resolve => {
2828
shell.cd(rootPath);
29-
const child = shell.exec('../packages/kyt-core/lib/index.js setup', (code, stdout) => {
30-
resolve({ code, output: stdout });
31-
});
29+
const child = shell.exec(
30+
'NODE_OPTIONS=--openssl-legacy-provider ../packages/kyt-core/lib/index.js setup',
31+
(code, stdout) => {
32+
resolve({ code, output: stdout });
33+
}
34+
);
3235
let skdone = false;
3336
let chooseDone = false;
3437
let ypmDone = false;

e2e_tests/tests/kyt-build.test.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ describe('kyt build', () => {
1010
it('should compile files into a build directory', () => {
1111
util.setupStageWithFixture(stageName, 'build-default');
1212

13-
const output = shell.exec('../packages/kyt-core/lib/index.js build');
13+
const output = shell.exec(
14+
'NODE_OPTIONS=--openssl-legacy-provider ../packages/kyt-core/lib/index.js build'
15+
);
1416

1517
expect(shell.test('-f', 'build/publicAssets.json')).toBe(true);
1618
expect(shell.test('-d', 'build/server')).toBe(true);
@@ -46,15 +48,15 @@ describe('kyt build', () => {
4648

4749
it('should ignore server build if hasServer=false', () => {
4850
util.setupStageWithFixture(stageName, 'build-no-server');
49-
const output = shell.exec('npm run build');
51+
const output = shell.exec('NODE_OPTIONS=--openssl-legacy-provider npm run build');
5052

5153
expect(output.code).toBe(0);
5254
expect(shell.test('-d', 'build/server')).toBe(false);
5355
});
5456

5557
it('should ignore client build if hasClient=false', () => {
5658
util.setupStageWithFixture(stageName, 'build-no-client');
57-
const output = shell.exec('npm run build');
59+
const output = shell.exec('NODE_OPTIONS=--openssl-legacy-provider npm run build');
5860

5961
expect(output.code).toBe(0);
6062
expect(shell.test('-d', 'build/server')).toBe(true);

e2e_tests/tests/starter-kyt.test.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ describe('starter kyts', () => {
1919
it('should start a dev server on :3000', () => {
2020
let outputTest;
2121
const run = new Promise(resolve => {
22-
const child = shell.exec(`${kytCli} dev`, () => {
22+
const child = shell.exec(`NODE_OPTIONS=--openssl-legacy-provider ${kytCli} dev`, () => {
2323
resolve(outputTest);
2424
});
2525
child.stdout.on('data', data => {
@@ -38,11 +38,14 @@ describe('starter kyts', () => {
3838

3939
it('should build and run', () => {
4040
let outputTest;
41-
shell.exec(`${kytCli} build`);
41+
shell.exec(`NODE_OPTIONS=--openssl-legacy-provider ${kytCli} build`);
4242
const run = new Promise(resolve => {
43-
const child = shell.exec('node build/server/main.js', () => {
44-
resolve(outputTest);
45-
});
43+
const child = shell.exec(
44+
'NODE_OPTIONS=--openssl-legacy-provider node build/server/main.js',
45+
() => {
46+
resolve(outputTest);
47+
}
48+
);
4649
child.stdout.on('data', data => {
4750
if (data.includes('✅ server started on port: 3000')) {
4851
shell.exec('sleep 5');
@@ -71,7 +74,7 @@ describe('starter kyts', () => {
7174
it('should start a server on :3001', () => {
7275
let outputTest;
7376
const run = new Promise(resolve => {
74-
const child = shell.exec(`${kytCli} dev`, () => {
77+
const child = shell.exec(`NODE_OPTIONS=--openssl-legacy-provider ${kytCli} dev`, () => {
7578
resolve(outputTest);
7679
});
7780
child.stdout.on('data', data => {
@@ -87,7 +90,7 @@ describe('starter kyts', () => {
8790
});
8891

8992
it('should build', () => {
90-
const output = shell.exec(`${kytCli} build`);
93+
const output = shell.exec(`NODE_OPTIONS=--openssl-legacy-provider ${kytCli} build`);
9194

9295
expect(output.stdout).toContain('✅ Done building');
9396

package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
"yarn": "^1"
1616
},
1717
"scripts": {
18-
"bootstrap": "yarn && lerna run prepare",
18+
"bootstrap": "yarn && lerna bootstrap && lerna run prepare && lerna link",
1919
"bootstrap:ci": "yarn && lerna run prepare",
2020
"clean-bootstrap": "lerna clean --yes && rm -rf node_modules && yarn bootstrap",
2121
"publish": "lerna publish",
22-
"publish:next": "lerna publish --preid next --dist-tag next",
22+
"publish:alpha": "lerna publish --canary --force-publish --preid alpha --dist-tag alpha",
2323
"test:starter": "lerna exec --scope \"kyt-starter-{server,static,universal}-src\" --",
2424
"test": "lerna run prepare && jest --detectOpenHandles && yarn test:starter yarn test --detectOpenHandles",
2525
"test:ci": "jest --detectOpenHandles --ci && yarn test:starter yarn test --detectOpenHandles --ci",
@@ -31,7 +31,10 @@
3131
"lint-staged": "yarn run lint -o",
3232
"lint-fix": "ESLINT_FIX=1 ESLINT_QUIET=1 yarn run lint",
3333
"lint:ci": "ESLINT_QUIET=1 yarn run lint",
34-
"watch-cli": "yarn workspace kyt watch",
34+
"watch": "run-p watch-*",
35+
"watch-kyt": "yarn workspace kyt watch",
36+
"watch-kyt-runtime": "yarn workspace kyt-runtime watch",
37+
"watch-kyt-utils": "yarn workspace kyt-utils watch",
3538
"prepare": "husky install"
3639
},
3740
"devDependencies": {
@@ -62,6 +65,7 @@
6265
"jest-runner-eslint": "0.10.1",
6366
"jest-silent-reporter": "0.5.0",
6467
"lerna": "4.0.0",
68+
"npm-run-all": "4.1.5",
6569
"prettier": "2.8.8",
6670
"react": "17.0.2",
6771
"react-dom": "17.0.2",

packages/kyt-core/package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"babel-preset-kyt-core": "2.0.1",
2424
"commander": "8.2.0",
2525
"core-js": "3.18.0",
26+
"css-loader": "5.2.7",
2627
"file-loader": "6.2.0",
2728
"filesize": "8.0.3",
2829
"find-babel-config": "1.2.0",
@@ -36,23 +37,28 @@
3637
"kyt-utils": "1.3.33",
3738
"lodash.clonedeep": "4.5.0",
3839
"lodash.merge": "4.6.2",
40+
"mini-css-extract-plugin": "1.6.2",
41+
"optimize-css-assets-webpack-plugin": "6.0.1",
42+
"postcss-loader": "4.2.0",
3943
"prop-types": "15.7.2",
4044
"ps-tree": "1.2.0",
4145
"react": "17.0.2",
4246
"react-dev-utils": "11.0.4",
4347
"react-error-overlay": "6.0.9",
4448
"regenerator-runtime": "0.13.9",
49+
"sass": "1.74.1",
50+
"sass-loader": "10.2.0",
4551
"semver": "7.3.5",
4652
"shelljs": "0.8.5",
4753
"simple-git": "2.42.0",
4854
"sockjs-client": "1.5.2",
4955
"source-map-support": "0.5.20",
5056
"strip-ansi": "6.0.0",
57+
"style-loader": "0.23.1",
5158
"url-loader": "4.1.1",
5259
"webpack": "4.46.0",
5360
"webpack-dev-server": "3.11.2",
5461
"webpack-merge": "4.2.2",
55-
"webpack-node-externals": "2.5.2",
5662
"webpackbar": "4.0.0"
5763
},
5864
"devDependencies": {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Works similarly to webpack-node-externals but it's not as
2+
// aggressive. It only excludes top-level dependency-declared
3+
// modules. Respects an `allowList` of regexp's that will be
4+
// used to match modules to include in the bundle.
5+
const { userPackageJSONPath } = require('kyt-utils/paths')();
6+
7+
let pkg;
8+
9+
try {
10+
// eslint-disable-next-line import/no-dynamic-require, global-require
11+
pkg = require(userPackageJSONPath);
12+
} catch (e) {
13+
pkg = {};
14+
}
15+
16+
module.exports = (allowList = []) => {
17+
// Get all of the dependencies from the package.json
18+
// and filter out the ones that are in the allowList
19+
const packageModules = [];
20+
Object.keys(pkg.dependencies || []).forEach(module => {
21+
allowList.forEach(allowedModule => {
22+
if (!allowedModule.test(module)) packageModules.push(module);
23+
});
24+
});
25+
26+
return [
27+
// eslint-disable-next-line consistent-return
28+
(context, request, callback) => {
29+
function getModuleName(requested) {
30+
const scopedModuleRegex = new RegExp(
31+
'@[a-zA-Z0-9][\\w-.]+/[a-zA-Z0-9][\\w-.]+([a-zA-Z0-9./]+)?',
32+
'g'
33+
);
34+
const req = requested;
35+
const delimiter = '/';
36+
37+
// Check if scoped module. For example: @company/boring-module
38+
if (scopedModuleRegex.test(req)) {
39+
// reset regexp
40+
scopedModuleRegex.lastIndex = 0;
41+
return req.split(delimiter, 2).join(delimiter);
42+
}
43+
return req.split(delimiter)[0];
44+
}
45+
if (packageModules.includes(getModuleName(request))) {
46+
// Mark this module as EXTERNAL
47+
return callback(null, `commonjs ${request}`);
48+
}
49+
// Include module in bundle / NOT EXTERNAL
50+
callback();
51+
},
52+
];
53+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
postcssOptions: {
3+
plugins: [['autoprefixer', {}]],
4+
},
5+
};

packages/kyt-core/src/config/webpack.dev.client.js

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
// Development webpack config for client code
22
const path = require('path');
33
const webpack = require('webpack');
4+
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
45
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
56
const { kytWebpackPlugins } = require('kyt-runtime/webpack');
6-
const { clientSrcPath, assetsBuildPath, publicBuildPath } = require('kyt-utils/paths')();
7+
const { clientSrcPath, assetsBuildPath, publicBuildPath, publicSrcPath } =
8+
require('kyt-utils/paths')();
79
const getPolyfill = require('./getPolyfill');
10+
const postcssLoader = require('../utils/getPostcssLoader');
811

912
module.exports = options => {
1013
const main = [
@@ -65,6 +68,40 @@ module.exports = options => {
6568
},
6669
},
6770

68-
plugins: [...kytWebpackPlugins(options), new webpack.HotModuleReplacementPlugin()],
71+
module: {
72+
rules: [
73+
{
74+
test: /\.module\.(sc|c)ss$/,
75+
use: [
76+
// 'style-loader',
77+
MiniCssExtractPlugin.loader,
78+
{
79+
loader: 'css-loader',
80+
options: {
81+
sourceMap: true,
82+
modules: {
83+
localIdentName: '[name]-[local]--[hash:base64:5]',
84+
// exportOnlyLocals: true,
85+
},
86+
},
87+
},
88+
postcssLoader,
89+
'sass-loader',
90+
],
91+
exclude: [publicSrcPath],
92+
},
93+
],
94+
},
95+
96+
plugins: [
97+
...kytWebpackPlugins(options),
98+
new MiniCssExtractPlugin({
99+
// Options similar to the same options in webpackOptions.output
100+
// both options are optional
101+
filename: '[name].css',
102+
chunkFilename: '[id].css',
103+
}),
104+
new webpack.HotModuleReplacementPlugin(),
105+
],
69106
};
70107
};

0 commit comments

Comments
 (0)