From 1db1da1dedb65589e239b3f991e31cd845d94e26 Mon Sep 17 00:00:00 2001 From: delambo Date: Sun, 7 Apr 2024 19:44:32 -0400 Subject: [PATCH 01/20] add css/scss modules support, back --- .mise.toml | 9 + .nvmrc | 1 - CONTRIBUTING.md | 18 +- package.json | 8 +- packages/kyt-core/package.json | 7 + .../kyt-core/src/config/postcss.config.js | 5 + .../kyt-core/src/config/webpack.dev.client.js | 40 +- .../kyt-core/src/config/webpack.dev.server.js | 25 +- .../src/config/webpack.prod.client.js | 44 +- .../src/config/webpack.prod.server.js | 25 +- .../kyt-core/src/utils/getPostcssLoader.js | 21 + packages/kyt-runtime/package.json | 1 + packages/kyt-runtime/src/server.js | 7 +- .../starter-src/package.json | 7 +- .../App/__snapshots__/index.test.js.snap | 86 +- .../starter-src/src/components/App/index.js | 13 +- .../starter-src/src/components/App/styled.js | 29 +- .../src/components/App/styles.module.scss | 31 + .../starter-src/src/server/template.js | 1 + .../starter-src/test/jest.test.config.js | 3 + packages/kyt-utils/package.json | 1 + packages/kyt-utils/src/paths.js | 1 + yarn.lock | 1098 ++++++++++++++++- 23 files changed, 1358 insertions(+), 123 deletions(-) create mode 100644 .mise.toml delete mode 100644 .nvmrc create mode 100644 packages/kyt-core/src/config/postcss.config.js create mode 100644 packages/kyt-core/src/utils/getPostcssLoader.js create mode 100644 packages/kyt-starter-universal/starter-src/src/components/App/styles.module.scss diff --git a/.mise.toml b/.mise.toml new file mode 100644 index 000000000..96627f78b --- /dev/null +++ b/.mise.toml @@ -0,0 +1,9 @@ +[tools] +node = '20.10.0' +# stuck on this version of yarn because of some weird problems +# related to babel tooling when installing new dependencies +yarn = '1.19.0' + +[env] +RTX_FETCH_REMOTE_VERSIONS_TIMEOUT="300 s" +NODE_OPTIONS = "--openssl-legacy-provider" diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index 53d838af2..000000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -lts/gallium diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4a03cd0e5..27a197ee5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,11 +20,25 @@ If you want to propose a large feature idea or architecture change you should co ## kyt local development -1. `nvm use` 1. Fork and clone `kyt` +1. [setup [mise](https://mise.jdx.dev/) and `mise install`] 1. Run `yarn bootstrap` to install the packages in the monorepo +1. Open a new shell and run `yarn watch` -[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: +Most changes are best to develop against the universal starter kyt: + +1. `cd packages/kyt-starter-universal/starter-src` +1. Run `yarn dev` or `yarn build` to test against kyt development and production builds + +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/runtimg. 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`. + +[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. + +### dev workflow + +1. open a new shell and start `yarn watch-cli` + +The following are some useful npm scripts for development: ### bootstrap diff --git a/package.json b/package.json index bae36190e..5105ecfcd 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "yarn": "^1" }, "scripts": { - "bootstrap": "yarn && lerna run prepare", + "bootstrap": "yarn && lerna bootstrap && lerna run prepare && lerna link", "bootstrap:ci": "yarn && lerna run prepare", "clean-bootstrap": "lerna clean --yes && rm -rf node_modules && yarn bootstrap", "publish": "lerna publish", @@ -31,7 +31,10 @@ "lint-staged": "yarn run lint -o", "lint-fix": "ESLINT_FIX=1 ESLINT_QUIET=1 yarn run lint", "lint:ci": "ESLINT_QUIET=1 yarn run lint", - "watch-cli": "yarn workspace kyt watch", + "watch": "run-p watch-*", + "watch-kyt": "yarn workspace kyt watch", + "watch-kyt-runtime": "yarn workspace kyt-runtime watch", + "watch-kyt-utils": "yarn workspace kyt-utils watch", "prepare": "husky install" }, "devDependencies": { @@ -62,6 +65,7 @@ "jest-runner-eslint": "0.10.1", "jest-silent-reporter": "0.5.0", "lerna": "4.0.0", + "npm-run-all": "4.1.5", "prettier": "2.8.8", "react": "17.0.2", "react-dom": "17.0.2", diff --git a/packages/kyt-core/package.json b/packages/kyt-core/package.json index 05dd56fbc..b50077a95 100644 --- a/packages/kyt-core/package.json +++ b/packages/kyt-core/package.json @@ -23,6 +23,7 @@ "babel-preset-kyt-core": "2.0.1", "commander": "8.2.0", "core-js": "3.18.0", + "css-loader": "5.2.7", "file-loader": "6.2.0", "filesize": "8.0.3", "find-babel-config": "1.2.0", @@ -36,18 +37,24 @@ "kyt-utils": "1.3.33", "lodash.clonedeep": "4.5.0", "lodash.merge": "4.6.2", + "mini-css-extract-plugin": "1.6.2", + "optimize-css-assets-webpack-plugin": "6.0.1", + "postcss-loader": "4.2.0", "prop-types": "15.7.2", "ps-tree": "1.2.0", "react": "17.0.2", "react-dev-utils": "11.0.4", "react-error-overlay": "6.0.9", "regenerator-runtime": "0.13.9", + "sass": "1.74.1", + "sass-loader": "10.2.0", "semver": "7.3.5", "shelljs": "0.8.5", "simple-git": "2.42.0", "sockjs-client": "1.5.2", "source-map-support": "0.5.20", "strip-ansi": "6.0.0", + "style-loader": "0.23.1", "url-loader": "4.1.1", "webpack": "4.46.0", "webpack-dev-server": "3.11.2", diff --git a/packages/kyt-core/src/config/postcss.config.js b/packages/kyt-core/src/config/postcss.config.js new file mode 100644 index 000000000..4e2f8e188 --- /dev/null +++ b/packages/kyt-core/src/config/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + postcssOptions: { + plugins: [['autoprefixer', {}]], + }, +}; diff --git a/packages/kyt-core/src/config/webpack.dev.client.js b/packages/kyt-core/src/config/webpack.dev.client.js index 7eb2f7a54..21875beb4 100644 --- a/packages/kyt-core/src/config/webpack.dev.client.js +++ b/packages/kyt-core/src/config/webpack.dev.client.js @@ -1,10 +1,13 @@ // Development webpack config for client code const path = require('path'); const webpack = require('webpack'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware'); const { kytWebpackPlugins } = require('kyt-runtime/webpack'); -const { clientSrcPath, assetsBuildPath, publicBuildPath } = require('kyt-utils/paths')(); +const { clientSrcPath, assetsBuildPath, publicBuildPath, publicSrcPath } = + require('kyt-utils/paths')(); const getPolyfill = require('./getPolyfill'); +const postcssLoader = require('../utils/getPostcssLoader'); module.exports = options => { const main = [ @@ -65,6 +68,39 @@ module.exports = options => { }, }, - plugins: [...kytWebpackPlugins(options), new webpack.HotModuleReplacementPlugin()], + module: { + rules: [ + { + test: /\.module\.(sc|c)ss$/, + use: [ + // 'style-loader', + MiniCssExtractPlugin.loader, + { + loader: 'css-loader', + options: { + modules: { + localIdentName: '[name]-[local]--[hash:base64:5]', + // exportOnlyLocals: true, + }, + }, + }, + // postcssLoader, + 'sass-loader', + ], + exclude: [publicSrcPath], + }, + ], + }, + + plugins: [ + ...kytWebpackPlugins(options), + new MiniCssExtractPlugin({ + // Options similar to the same options in webpackOptions.output + // both options are optional + filename: '[name].css', + chunkFilename: '[id].css', + }), + new webpack.HotModuleReplacementPlugin(), + ], }; }; diff --git a/packages/kyt-core/src/config/webpack.dev.server.js b/packages/kyt-core/src/config/webpack.dev.server.js index 60ad6fb36..8d291a37b 100644 --- a/packages/kyt-core/src/config/webpack.dev.server.js +++ b/packages/kyt-core/src/config/webpack.dev.server.js @@ -2,9 +2,10 @@ const webpack = require('webpack'); const nodeExternals = require('webpack-node-externals'); -const { serverSrcPath, serverBuildPath, clientAssetsFile, loadableAssetsFile } = +const { serverSrcPath, serverBuildPath, clientAssetsFile, loadableAssetsFile, publicSrcPath } = require('kyt-utils/paths')(); const StartServerPlugin = require('./StartServerPlugin'); +const postcssLoader = require('../utils/getPostcssLoader'); const getPolyfill = require('./getPolyfill'); const nodeArgs = ['-r', 'source-map-support/register', '--max_old_space_size=4096']; @@ -66,6 +67,28 @@ module.exports = options => { libraryTarget: 'commonjs2', }, + module: { + rules: [ + { + test: /\.module\.(sc|c)ss$/, + use: [ + { + loader: 'css-loader', + options: { + modules: { + localIdentName: '[name]-[local]--[hash:base64:5]', + exportOnlyLocals: true, + }, + }, + }, + postcssLoader, + 'sass-loader', + ], + exclude: [publicSrcPath], + }, + ], + }, + plugins: [ // Prevent creating multiple chunks for the server new webpack.optimize.LimitChunkCountPlugin({ diff --git a/packages/kyt-core/src/config/webpack.prod.client.js b/packages/kyt-core/src/config/webpack.prod.client.js index bb74e3e67..35aa18733 100644 --- a/packages/kyt-core/src/config/webpack.prod.client.js +++ b/packages/kyt-core/src/config/webpack.prod.client.js @@ -1,7 +1,9 @@ // Production webpack config for client code - -const { clientSrcPath, assetsBuildPath } = require('kyt-utils/paths')(); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); +const { clientSrcPath, assetsBuildPath, publicSrcPath } = require('kyt-utils/paths')(); const { kytWebpackPlugins } = require('kyt-runtime/webpack'); +const postcssLoader = require('../utils/getPostcssLoader'); const getPolyfill = require('./getPolyfill'); module.exports = options => ({ @@ -22,7 +24,43 @@ module.exports = options => ({ publicPath: options.publicPath, }, - plugins: [...kytWebpackPlugins(options)], + module: { + rules: [ + { + test: /\.module\.(sc|c)ss$/, + use: [ + MiniCssExtractPlugin.loader, + { + loader: 'css-loader', + options: { + modules: { + localIdentName: '[name]-[local]--[hash:base64:5]', + }, + }, + }, + postcssLoader, + { + loader: 'sass-loader', + options: { + sourceMap: true, + }, + }, + ], + exclude: [publicSrcPath], + }, + ], + }, + + plugins: [ + ...kytWebpackPlugins(options), + + new MiniCssExtractPlugin({ + filename: '[name]-[contenthash].css', + chunkFilename: '[name]-[contenthash].css', + }), + + new OptimizeCSSAssetsPlugin({}), + ], optimization: { moduleIds: 'hashed', diff --git a/packages/kyt-core/src/config/webpack.prod.server.js b/packages/kyt-core/src/config/webpack.prod.server.js index 659fd3d80..0215e6706 100644 --- a/packages/kyt-core/src/config/webpack.prod.server.js +++ b/packages/kyt-core/src/config/webpack.prod.server.js @@ -2,7 +2,8 @@ const webpack = require('webpack'); const nodeExternals = require('webpack-node-externals'); -const { serverSrcPath, serverBuildPath } = require('kyt-utils/paths')(); +const { serverSrcPath, serverBuildPath, publicSrcPath } = require('kyt-utils/paths')(); +const postcssLoader = require('../utils/getPostcssLoader'); const getPolyfill = require('./getPolyfill'); module.exports = options => { @@ -47,6 +48,28 @@ module.exports = options => { libraryTarget: 'commonjs2', }, + module: { + rules: [ + { + test: /\.module\.(sc|c)ss$/, + use: [ + { + loader: 'css-loader', + options: { + modules: { + localIdentName: '[name]-[local]--[hash:base64:5]', + exportOnlyLocals: true, + }, + }, + }, + postcssLoader, + 'sass-loader', + ], + exclude: [publicSrcPath], + }, + ], + }, + plugins: [ new webpack.BannerPlugin({ banner: 'require("source-map-support").install();', diff --git a/packages/kyt-core/src/utils/getPostcssLoader.js b/packages/kyt-core/src/utils/getPostcssLoader.js new file mode 100644 index 000000000..102faef5a --- /dev/null +++ b/packages/kyt-core/src/utils/getPostcssLoader.js @@ -0,0 +1,21 @@ +const fs = require('fs'); +const { userPostcssConfigPath } = require('kyt-utils/paths')(); +const logger = require('kyt-utils/logger'); +const kytPostcssConfig = require('../config/postcss.config'); + +// We either use the kyt postcss.config.js or we use an +// override from the user. +const postcssConfig = { loader: 'postcss-loader' }; +const userHasPostcssConfig = fs.existsSync(userPostcssConfigPath); + +if (userHasPostcssConfig) { + logger.info(`Using postcss config: ${userPostcssConfigPath}`); + // eslint-disable-next-line global-require,import/no-dynamic-require + const userPostcssConfig = require(userPostcssConfigPath); + postcssConfig.options = + typeof userPostcssConfig === 'function' ? userPostcssConfig() : userPostcssConfig; +} else { + postcssConfig.options = kytPostcssConfig; +} + +module.exports = postcssConfig; \ No newline at end of file diff --git a/packages/kyt-runtime/package.json b/packages/kyt-runtime/package.json index 8589c41be..c43daff0b 100644 --- a/packages/kyt-runtime/package.json +++ b/packages/kyt-runtime/package.json @@ -31,6 +31,7 @@ "babel-preset-kyt-core": "2.0.1" }, "scripts": { + "watch": "yarn prepare --watch", "prepare": "rimraf lib && babel src -d lib --ignore \"**/__tests__/**\"" }, "gitHead": "a120343756f132868e6af0cfee3579bfc3dafaa8" diff --git a/packages/kyt-runtime/src/server.js b/packages/kyt-runtime/src/server.js index 40d61eb48..de867fc05 100644 --- a/packages/kyt-runtime/src/server.js +++ b/packages/kyt-runtime/src/server.js @@ -21,12 +21,14 @@ export const getBundles = ({ entry = 'main', modules, assets = null, loadableBun const runtimeBundle = assets[`runtime~${entry}.js`]; const entryBundle = assets[`${entry}.js`]; const vendorBundle = assets['vendor.js']; + const cssBundle = assets[`${entry}.css`]; const bundleManifest = { runtimeBundle, entryBundle, vendorBundle, scripts: [], + cssBundle, }; if (!modules || modules.length === 0) { @@ -40,11 +42,14 @@ export const getBundles = ({ entry = 'main', modules, assets = null, loadableBun let hashes = []; loadableBundles.entries.forEach(key => { - hashes = hashes.concat([assets[`${key}.js`]]).filter(Boolean); + hashes = hashes.concat([assets[`${key}.js`], assets[`${key}.css`]]).filter(Boolean); }); const bundles = getLoadableBundles(loadableBundles.bundles, modules); + const cssBundles = bundles.filter(b => b.file.endsWith('.css') && !hashes.includes(b.publicPath)); + bundleManifest.styles = [...new Set(cssBundles.map(b => b.publicPath))]; + const jsBundles = bundles.filter( b => b?.file?.endsWith('.js') && diff --git a/packages/kyt-starter-universal/starter-src/package.json b/packages/kyt-starter-universal/starter-src/package.json index a8d1b3281..c11873660 100644 --- a/packages/kyt-starter-universal/starter-src/package.json +++ b/packages/kyt-starter-universal/starter-src/package.json @@ -14,9 +14,9 @@ }, "homepage": "/~https://github.com/nytimes/kyt/packages/kyt-starter-universal#readme", "scripts": { - "dev": "kyt dev", - "build": "kyt build", - "start": "node build/server/main.js", + "dev": "NODE_OPTIONS=--openssl-legacy-provider kyt dev", + "build": "NODE_OPTIONS=--openssl-legacy-provider kyt build", + "start": "NODE_OPTIONS=--openssl-legacy-provider node build/server/main.js", "lint": "eslint .", "lint-fix": "eslint . --fix", "test": "jest", @@ -56,6 +56,7 @@ "eslint-plugin-prettier": "3.4.1", "eslint-plugin-react": "7.26.0", "eslint-plugin-react-hooks": "4.2.0", + "identity-obj-proxy": "3.0.0", "jest": "27.2.1", "jest-preset-kyt-rtl": "1.1.2", "jest-preset-kyt-styled": "1.4.12", diff --git a/packages/kyt-starter-universal/starter-src/src/components/App/__snapshots__/index.test.js.snap b/packages/kyt-starter-universal/starter-src/src/components/App/__snapshots__/index.test.js.snap index 13551ea30..3728a3ef2 100644 --- a/packages/kyt-starter-universal/starter-src/src/components/App/__snapshots__/index.test.js.snap +++ b/packages/kyt-starter-universal/starter-src/src/components/App/__snapshots__/index.test.js.snap @@ -1,28 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`App Test home route 1`] = ` -.lights-5 { - width: 500px; - margin: 0 auto; - padding: 15px; - text-align: center; -} - -.lights-8 { - width: 500px; - margin: 15px auto; -} - -.lights-2 { - display: inline-block; -} - -.lights-1 { - padding: 15px; - color: #00a68f; - font-size: 18px; -} - .lights-0 { display: block; width: 252px; @@ -31,7 +9,7 @@ exports[`App Test home route 1`] = ` background: url(file-stub); } -.lights-6 + .lights-6 { +.lights-1 + .lights-1 { padding-top: 15px; } @@ -40,23 +18,23 @@ exports[`App Test home route 1`] = ` class="lights-0" />

Welcome to the @@ -77,7 +55,7 @@ exports[`App Test home route 1`] = ` . This starter kyt should serve as the base for an advanced, server-rendered React app.

Check out the Tools section for an outline of the libraries that are used in this Starter-kyt.

@@ -93,28 +71,6 @@ Array [ `; exports[`App Test tools route 1`] = ` -.lights-5 { - width: 500px; - margin: 0 auto; - padding: 15px; - text-align: center; -} - -.lights-11 { - width: 500px; - margin: 15px auto; -} - -.lights-2 { - display: inline-block; -} - -.lights-1 { - padding: 15px; - color: #00a68f; - font-size: 18px; -} - .lights-0 { display: block; width: 252px; @@ -123,7 +79,7 @@ exports[`App Test tools route 1`] = ` background: url(file-stub); } -.lights-6 + .lights-6 { +.lights-1 + .lights-1 { padding-top: 5px; } @@ -132,23 +88,23 @@ exports[`App Test tools route 1`] = ` class="lights-0" />