diff --git a/.eslintignore b/.eslintignore index c2f7ebed02c..4979a05becc 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,5 +2,9 @@ node_modules /.sewing-kit /examples /build -/dist +/build-internal /src/styles/polaris-tokens + +index.esnext +index.js +index.mjs diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 5c3d6938aba..95822aa7545 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -95,7 +95,6 @@ After cloning Polaris React, run `yarn` to fetch its dependencies. Then you can - `yarn dev` runs a Storybook server which includes a playground editable at `playground/Playground.tsx` - `yarn test` runs the complete test suite -- `yarn test:coverage` runs tests and generates a test coverage report - `yarn test ` runs tests with matching filenames - `yarn tophat` runs a local development server and mounts all of the component examples diff --git a/.gitignore b/.gitignore index 093a6617773..8671b5744ab 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,12 @@ node_modules /.sewing-kit /build -/dist +/build-internal .DS_Store *.scss.d.ts /src/styles/polaris-tokens .idea + +index.esnext +index.js +index.mjs diff --git a/.prettierignore b/.prettierignore index ac9ac12ea54..ed6e726d0c5 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,7 +1,11 @@ node_modules /.sewing-kit /build -/dist +/build-internal /src/styles/polaris-tokens /package.json + +index.esnext +index.js +index.mjs diff --git a/.storybook/main.js b/.storybook/main.js index a48164011f7..a58d765d8ed 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -30,7 +30,7 @@ module.exports = { options: { cacheDirectory: path.resolve( __dirname, - '../build/cache/storybook/markdown', + '../build-internal/cache/storybook/markdown', ), }, }, @@ -71,7 +71,7 @@ module.exports = { config.plugins.push( new CreateFileWebpack({ - path: './build/storybook/static/services/', + path: './build-internal/storybook/static/services/', fileName: 'ping.html', content: 'OK', diff --git a/.stylelintignore b/.stylelintignore index a2d21eef76c..4d0f64c97f1 100644 --- a/.stylelintignore +++ b/.stylelintignore @@ -1,5 +1,5 @@ node_modules /.sewing-kit /build -/dist +/build-internal /src/styles/polaris-tokens diff --git a/.vscode/settings.json b/.vscode/settings.json index 03857c25ffa..1826618d23c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,7 +11,7 @@ "**/.git": true, "**/node_modules": true, ".{dev,shadowenv.d,sewing-kit}": true, - "{build,dist}/": true, + "{build,build-internal}/": true, "examples/*/build": true }, "javascript.validate.enable": false, @@ -20,7 +20,7 @@ "search.exclude": { "**/node_modules": true, ".{dev,shadowenv.d}": true, - "{build,dist}/": true, + "{build,build-internal}/": true, "examples/*/build": true }, "typescript.tsdk": "./node_modules/typescript/lib" diff --git a/README.md b/README.md index 3f4f91c9ce8..824dbb9fba2 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ yarn add @shopify/polaris 1. Import the CSS directly into your project if your asset packager supports it: ```js -import '@shopify/polaris/dist/styles.css'; +import '@shopify/polaris/build/esm/styles.css'; ``` Otherwise include the CSS in your HTML. We suggest copying the styles file into your own project, but you may also use it directly: @@ -39,7 +39,7 @@ Otherwise include the CSS in your HTML. We suggest copying the styles file into ```html ``` @@ -76,7 +76,7 @@ If React doesn’t make sense for your application, you can use a CSS-only versi ```html ``` diff --git a/UNRELEASED-v7.md b/UNRELEASED-v7.md index 82e5ccd3448..c5a469308ba 100644 --- a/UNRELEASED-v7.md +++ b/UNRELEASED-v7.md @@ -11,6 +11,8 @@ Use [the changelog guidelines](https://git.io/polaris-changelog-guidelines) to f - `Autocomplete` now requires `Autocomplete.TextField` to be used ([#3910](https://github.com/Shopify/polaris-react/pull/3910)) - Removed ComboBox as a named export on `Autocomplete` ([#3910](https://github.com/Shopify/polaris-react/pull/3910)) - Remove the `esnext` folder from the package. If you use Polaris in an app built with sewing-kit, it must use at least sewing-kit 0.152.0 to leverage esnext builds. ([#4425](https://github.com/Shopify/polaris-react/pull/4425)) +- The component styles have moved fromm `dist/styles.css` to `build/esm/styles.css`. Consumers who import styles shall need to update their import path. ([#4424](https://github.com/Shopify/polaris-react/pull/4424)) +- The public Sass API has moved from `dist/styles/_public-api.scss` to `build/styles/_public-api.scss`. Consumers who use our Sass API shall need to update their import path. ([#4424](https://github.com/Shopify/polaris-react/pull/4424)) ### Enhancements diff --git a/config/rollup/plugin-styles.js b/config/rollup/plugin-styles.js index 4b4110ae7f6..0a58e4dc51b 100644 --- a/config/rollup/plugin-styles.js +++ b/config/rollup/plugin-styles.js @@ -7,12 +7,12 @@ import postcss from 'postcss'; import cssModules from 'postcss-modules'; export function styles({ - output, + output = '', plugins = [], modules = {}, mode, include = ['**/*.css', '**/*.scss'], - exclude, + exclude = [], } = {}) { if (!['standalone', 'esnext'].includes(mode)) { throw new Error( @@ -76,10 +76,10 @@ export function styles({ function generateBundleStandalone(rollup, generateOptions, bundle) { // generateBundle gets called once per call to bundle.write(). We call - // that twice - once for the commonjs build (the index.js file), once for + // that twice - once for the commonjs build (the cjs folder), once for // the esm build (the esm folder). We only want to do perform this logic - // once in the commonjs build - if (!(generateOptions.file && generateOptions.format === 'cjs')) { + // once in the esm build + if (!generateOptions.dir.endsWith('/build/esm')) { return; } @@ -91,9 +91,12 @@ export function styles({ // resolved. This may change between runs so we can't rely on it. // The contents of the emitted css file should use the order in which the // files were referenced in the compiled javascript, which can be obtained - // by looking at bundles[].modules. - const bundleModuleIds = flatMap(Object.values(bundle), (fileInfo) => - Object.keys(fileInfo.modules), + // by looking at the imports of each entrypoint's bundle information. + const entrypointBundles = Object.values(bundle).filter( + (bundleInfo) => bundleInfo.isEntry, + ); + const bundleModuleIds = flatMap(entrypointBundles, (bundleInfo) => + getRecursiveImportOrder(bundleInfo.facadeModuleId, rollup.getModuleInfo), ); const missingReferences = Object.keys(cssByFile).filter( @@ -212,3 +215,26 @@ function hoistCharsetDeclaration(css) { return `${standaloneCssFileCharset}${result}`; } + +/** + * Recursivly get the correct import order from rollup + * We only process a file once + * + * @param {string} id + * @param {Function} getModuleInfo + * @param {Set} seen + */ +function getRecursiveImportOrder(id, getModuleInfo, seen = new Set()) { + if (seen.has(id)) { + return []; + } + + seen.add(id); + + const result = [id]; + getModuleInfo(id).importedIds.forEach((importFile) => { + result.push(...getRecursiveImportOrder(importFile, getModuleInfo, seen)); + }); + + return result; +} diff --git a/dev.yml b/dev.yml index 0c450fdc759..e740ce9aa11 100644 --- a/dev.yml +++ b/dev.yml @@ -9,7 +9,6 @@ up: open: app: http://localhost:6006/ shipit: https://shipit.shopify.io/shopify/polaris-react/production - coverage: build/coverage/index.html commands: # People new to the project might be tempted to run some @@ -21,5 +20,4 @@ commands: server: yarn run dev style: yarn run lint test: yarn run test - test:coverage: yarn run test:coverage type-check: yarn run type-check diff --git a/package.json b/package.json index ada98232c12..181b433dc78 100644 --- a/package.json +++ b/package.json @@ -14,14 +14,16 @@ "access": "public" }, "files": [ - "dist", - "locales", - "!*.tsbuildinfo" + "build/", + "!build/ts/**/*.tsbuildinfo", + "!build/ts/**/tests/", + "!build/ts/**/playground/", + "locales/" ], "sideEffects": [ "**/*.css", "**/*.scss", - "**/configure.{js,ts,ts.esnext}" + "**/configure.{js,mjs,esnext,ts}" ], "keywords": [ "shopify", @@ -30,32 +32,29 @@ "components", "component library" ], - "main": "dist/index.js", - "module": "dist/esm/index.js", - "esnext": "dist/esnext/index.ts.esnext", - "types": "dist/types/latest/src/index.d.ts", + "main": "build/cjs/index.js", + "module": "build/esm/index.js", + "esnext": "build/esnext/index.ts.esnext", + "types": "build/ts/latest/src/index.d.ts", "typesVersions": { "<4.0": { - "dist/types/latest/*": [ - "dist/types/3.4/*" + "build/types/latest/*": [ + "build/ts/3.4/*" ] } }, "scripts": { "lint": "skn lint", - "format": "skn format", + "format": "skn lint --fix", "type-check": "skn type-check", "test": "skn test", - "test:coverage": "CI=true yarn test --coverage", - "posttest:coverage": "yarn open:coverage", - "open:coverage": "open build/coverage/index.html", "check": "npm-run-all lint type-check check:custom-properties test", "build:known-custom-properties": "node ./scripts/build-analyze-custom-properties", "precheck:custom-properties": "yarn run build:known-custom-properties", "preoutput-custom-properties": "yarn run build:known-custom-properties", - "output-custom-properties": "node ./build/custom-properties/cli.js -o build/custom-properties-data.json -i build/known-custom-properties.json --pattern 'src/**/{,!(_utilities|_interaction-state)*}.scss'", - "check:custom-properties": "node ./build/custom-properties/cli.js -l error -i build/known-custom-properties.json --pattern 'src/**/{,!(_utilities|_interaction-state)*}.scss'", - "clean": "rimraf build dist \"./src/styles/polaris-tokens\"", + "output-custom-properties": "node ./build-internal/custom-properties/cli.js -o build-internal/custom-properties-data.json -i build-internal/known-custom-properties.json --pattern 'src/**/{,!(_utilities|_interaction-state)*}.scss'", + "check:custom-properties": "node ./build-internal/custom-properties/cli.js -l error -i build-internal/known-custom-properties.json --pattern 'src/**/{,!(_utilities|_interaction-state)*}.scss'", + "clean": "rimraf build build-internal \"./src/styles/polaris-tokens\"", "build": "node ./scripts/build.js", "prebuild-consumer": "yarn run build", "build-consumer": "node ./scripts/build-consumer", @@ -65,11 +64,11 @@ "copy-polaris-tokens": "rimraf ./src/styles/polaris-tokens && shx cp -r ./node_modules/@shopify/polaris-tokens/dist ./src/styles/polaris-tokens", "prepublishOnly": "yarn run build", "dev": "npm-run-all copy-polaris-tokens storybook", - "start": "serve ./build/storybook/static -l ${PORT:=6006}", + "start": "serve ./build-internal/storybook/static -l ${PORT:=6006}", "readme-update-version": "node ./scripts/readme-update-version", "version": "yarn run readme-update-version", "storybook": "start-storybook -p 6006 --quiet", - "storybook:build": "yarn run copy-polaris-tokens && build-storybook -o build/storybook/static", + "storybook:build": "yarn run copy-polaris-tokens && build-storybook -o build-internal/storybook/static", "precolordocs": "yarn run build", "colordocs": "node ./scripts/color-system-docs", "postcolordocs": "yarn run format" @@ -92,10 +91,7 @@ "devDependencies": { "@babel/core": "^7.15.0", "@babel/node": "^7.14.9", - "@rollup/plugin-babel": "^5.1.0", - "@rollup/plugin-commonjs": "^14.0.0", "@rollup/plugin-image": "^2.0.5", - "@rollup/plugin-node-resolve": "^8.4.0", "@rollup/plugin-replace": "^2.3.3", "@rollup/pluginutils": "^3.1.0", "@sewing-kit/cli": "^0.6.2", @@ -103,7 +99,9 @@ "@sewing-kit/plugin-babel": "^0.1.1", "@sewing-kit/plugin-eslint": "^0.4.2", "@sewing-kit/plugin-jest": "^0.5.1", + "@sewing-kit/plugin-package-build": "^0.5.1", "@sewing-kit/plugin-prettier": "^0.1.2", + "@sewing-kit/plugin-rollup": "^0.3.3", "@sewing-kit/plugin-stylelint": "^0.4.2", "@sewing-kit/plugin-typescript": "^0.6.1", "@shopify/babel-preset": "^24.1.2", @@ -155,7 +153,6 @@ "react-is": "^16.7.2", "react-test-renderer": "^16.14.0", "rimraf": "^3.0.0", - "rollup": "^2.22.2", "sass-loader": "^8.0.0", "scss-parser": "^1.0.3", "semver": "^6.3.0", @@ -184,22 +181,22 @@ "size-limit": [ { "name": "cjs", - "path": "dist/index.js", + "path": "build/cjs/index.js", "limit": "165 kB" }, { "name": "esm", - "path": "dist/esm/index.js", + "path": "build/esm/index.js", "limit": "105 kB" }, { "name": "esnext", - "path": "dist/esnext/index.ts.esnext", + "path": "build/esnext/index.esnext", "limit": "160 kB" }, { "name": "css", - "path": "dist/styles.css", + "path": "build/esm/styles.css", "limit": "45 kB" } ] diff --git a/rollup.config.js b/rollup.config.js deleted file mode 100644 index c766e9457d4..00000000000 --- a/rollup.config.js +++ /dev/null @@ -1,94 +0,0 @@ -import commonjs from '@rollup/plugin-commonjs'; -import nodeResolve from '@rollup/plugin-node-resolve'; -import replace from '@rollup/plugin-replace'; -import babel from '@rollup/plugin-babel'; -import image from '@rollup/plugin-image'; -import postcssShopify from '@shopify/postcss-plugin'; - -import packageJSON from './package.json'; -import {styles} from './config/rollup/plugin-styles'; -import {generateScopedName} from './config/rollup/namespaced-classname'; - -const root = __dirname; - -const externalPackages = [ - ...Object.keys(packageJSON.dependencies), - ...Object.keys(packageJSON.peerDependencies), -]; - -function external(id) { - return externalPackages.some((aPackage) => id.startsWith(aPackage)); -} - -function plugins({browserslist, stylesConfig}) { - return [ - replace({ - '{{POLARIS_VERSION}}': packageJSON.version, - delimiters: ['', ''], - }), - nodeResolve({ - extensions: ['.js', '.jsx', '.ts', '.tsx'], - }), - commonjs(), - babel({ - extensions: ['.js', '.jsx', '.ts', '.tsx'], - envName: 'production', - exclude: 'node_modules/**', - babelHelpers: 'bundled', - // This is kinda silly but if the browserslist is absent we don't want - // to set the key at all (which is different to the key being present but - // it's value is undefined) - ...(browserslist ? {targets: browserslist} : {}), - }), - styles({ - ...stylesConfig, - plugins: [postcssShopify], - }), - image(), - ]; -} - -// eslint-disable-next-line import/no-default-export, import/no-anonymous-default-export -export default [ - { - input: `${root}/src/index.ts`, - output: [ - {format: 'cjs', file: `${root}/dist/index.js`}, - {format: 'esm', dir: `${root}/dist/esm`, preserveModules: true}, - ], - plugins: plugins({ - // Not specifying a browserslist config here to use the default as - // defined in our package.json - stylesConfig: { - mode: 'standalone', - output: 'styles.css', - modules: { - generateScopedName: generateScopedName({includeHash: false}), - }, - }, - }), - external, - }, - { - input: `${root}/src/index.ts`, - output: [ - { - format: 'esm', - dir: `${root}/dist/esnext`, - entryFileNames: '[name][extname].esnext', - }, - ], - preserveModules: true, - plugins: plugins({ - browserslist: 'extends @shopify/browserslist-config/latest-evergreen', - stylesConfig: { - mode: 'esnext', - modules: { - generateScopedName: generateScopedName({includeHash: true}), - }, - }, - }), - - external, - }, -]; diff --git a/scripts/accessibility-check.js b/scripts/accessibility-check.js index d570852eb26..ee198132fc4 100644 --- a/scripts/accessibility-check.js +++ b/scripts/accessibility-check.js @@ -8,7 +8,7 @@ const {storybookA11yTest} = require('@shopify/storybook-a11y-test'); iframePath: path.join( 'file://', __dirname, - '../build/storybook/static/iframe.html', + '../build-internal/storybook/static/iframe.html', ), skippedStoryIds: ['playground-playground'], }; diff --git a/scripts/analyze-custom-properties/tsconfig-partial-build.json b/scripts/analyze-custom-properties/tsconfig-partial-build.json index dad05a8ee63..5e415a31be6 100644 --- a/scripts/analyze-custom-properties/tsconfig-partial-build.json +++ b/scripts/analyze-custom-properties/tsconfig-partial-build.json @@ -4,8 +4,9 @@ "compilerOptions": { "noEmit": false, "noEmitHelpers": false, + "emitDeclarationOnly": false, "module": "commonjs", - "outDir": "../../build/custom-properties-partial-build" + "outDir": "../../build-internal/custom-properties-partial-build" }, "include": ["../../src/utilities/custom-properties"] } diff --git a/scripts/analyze-custom-properties/tsconfig.json b/scripts/analyze-custom-properties/tsconfig.json index 8440433ad04..a91cb904a37 100644 --- a/scripts/analyze-custom-properties/tsconfig.json +++ b/scripts/analyze-custom-properties/tsconfig.json @@ -2,7 +2,7 @@ "extends": "@shopify/typescript-configs/library", "compilerOptions": { "noEmit": false, - "outDir": "../../build/custom-properties", + "outDir": "../../build-internal/custom-properties", "noEmitHelpers": false, "baseUrl": ".", "rootDir": ".", diff --git a/scripts/build-analyze-custom-properties.js b/scripts/build-analyze-custom-properties.js index 6696c5ccc74..3d12123366a 100644 --- a/scripts/build-analyze-custom-properties.js +++ b/scripts/build-analyze-custom-properties.js @@ -26,10 +26,10 @@ execSync(`${tscBin} --project ${partialBuildTsConfig}`, execOptions); const { nonDesignLangaugeCustomProperties, designLangaugeCustomProperties, -} = require('../build/custom-properties-partial-build/src/utilities/custom-properties'); +} = require('../build-internal/custom-properties-partial-build/src/utilities/custom-properties'); writeFileSync( - resolvePath(root, 'build/known-custom-properties.json'), + resolvePath(root, 'build-internal/known-custom-properties.json'), JSON.stringify( [...designLangaugeCustomProperties, ...nonDesignLangaugeCustomProperties], null, diff --git a/scripts/build-validate.js b/scripts/build-validate.js index 181b5d9434d..45270397940 100644 --- a/scripts/build-validate.js +++ b/scripts/build-validate.js @@ -16,42 +16,52 @@ validateVersionReplacement(); function validateStandardBuild() { // Standard build - assert.ok(fs.existsSync('./dist/index.js')); - assert.ok(fs.existsSync('./dist/esm/index.js')); - assert.ok(fs.existsSync('./dist/styles.css')); + assert.ok(fs.existsSync('./build/cjs/index.js')); + assert.ok(fs.existsSync('./build/esm/index.mjs')); + assert.ok(fs.existsSync('./build/esm/styles.css')); // Assert it uses named exports rather than properties from the React default // export to help tree-shaking. // React.createElement and React.Fragment are the allowed exceptions - const esModuleContent = fs.readFileSync('./dist/index.js', 'utf-8'); - const unwantedReactUsageMatches = - esModuleContent.match( - /React__default\.(?!createElement|Fragment)[A-Za-z0-9]+/g, - ) || []; + const files = glob.sync('./build/cjs/**/*.js'); + assert.notStrictEqual(files.length, 0); + const filesContainingUnwantedReactUsage = []; + files.forEach((file) => { + const content = fs.readFileSync(file, 'utf-8'); + + const unwantedReactUsageMatches = + content.match( + /React__default\['default'\]\.(?!createElement|Fragment)[A-Za-z0-9]+/g, + ) || []; + + if (unwantedReactUsageMatches.length) { + filesContainingUnwantedReactUsage.push(file); + } + }); - assert.deepStrictEqual(unwantedReactUsageMatches, []); + assert.deepStrictEqual(filesContainingUnwantedReactUsage, []); // Standard build css contains namespaced classes - const cssContent = fs.readFileSync('./dist/styles.css', 'utf-8'); + const cssContent = fs.readFileSync('./build/esm/styles.css', 'utf-8'); assert.ok(cssContent.includes('.Polaris-Avatar{')); assert.ok(cssContent.includes('.Polaris-BulkActions__BulkActionButton{')); } function validateEsNextBuild() { // ESnext build - assert.ok(fs.existsSync('./dist/esnext/index.ts.esnext')); - assert.ok(fs.existsSync('./dist/esnext/components/Avatar/Avatar.tsx.esnext')); - assert.ok(fs.existsSync('./dist/esnext/components/Avatar/Avatar.css')); + assert.ok(fs.existsSync('./build/esnext/index.esnext')); + assert.ok(fs.existsSync('./build/esnext/components/Avatar/Avatar.esnext')); + assert.ok(fs.existsSync('./build/esnext/components/Avatar/Avatar.css')); // ESnext build css contains namespaced classes, and const cssContent = fs.readFileSync( - './dist/esnext/components/Avatar/Avatar.css', + './build/esnext/components/Avatar/Avatar.css', 'utf-8', ); assert.ok(cssContent.includes('.Polaris-Avatar_z763p{')); const jsContent = fs.readFileSync( - './dist/esnext/components/Avatar/Avatar.scss.esnext', + './build/esnext/components/Avatar/Avatar.scss.esnext', 'utf-8', ); @@ -61,11 +71,11 @@ function validateEsNextBuild() { } function validateSassPublicApi() { - assert.ok(fs.existsSync('./dist/styles/_public-api.scss')); - assert.ok(fs.existsSync('./dist/styles/foundation/_spacing.scss')); + assert.ok(fs.existsSync('./build/styles/_public-api.scss')); + assert.ok(fs.existsSync('./build/styles/foundation/_spacing.scss')); // does not contain any :global definitions - const files = glob.sync(`./dist/styles/**/*.scss`); + const files = glob.sync(`./build/styles/**/*.scss`); assert.notStrictEqual(files.length, 0); const filesWithGlobalDefinitions = files.filter((file) => { @@ -76,13 +86,13 @@ function validateSassPublicApi() { } function validateAncillaryOutput() { - assert.ok(fs.existsSync('./dist/types/latest/src/index.d.ts')); + assert.ok(fs.existsSync('./build/ts/latest/src/index.d.ts')); // Downleveled for consumers on older TypeScript versions - assert.ok(fs.existsSync('./dist/types/3.4/src/index.d.ts')); + assert.ok(fs.existsSync('./build/ts/3.4/src/index.d.ts')); } function validateVersionReplacement() { - const files = glob.sync('./dist/**/*.{js,mjs,esnext,css,scss}'); + const files = glob.sync('./build/**/*.{js,mjs,esnext,css,scss}'); assert.notStrictEqual(files.length, 0); @@ -106,10 +116,10 @@ function validateVersionReplacement() { assert.strictEqual(fileBuckets.includesTemplateString.length, 0); assert.deepStrictEqual(fileBuckets.includesVersion, [ - './dist/esm/configure.js', - './dist/esnext/components/AppProvider/AppProvider.css', - './dist/esnext/configure.ts.esnext', - './dist/index.js', - './dist/styles.css', + './build/cjs/configure.js', + './build/esm/configure.mjs', + './build/esm/styles.css', + './build/esnext/components/AppProvider/AppProvider.css', + './build/esnext/configure.esnext', ]); } diff --git a/scripts/build.js b/scripts/build.js index 4f59187904e..43bbcc3dc00 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -4,24 +4,8 @@ const {resolve} = require('path'); const root = resolve(__dirname, '..'); const run = (cmd) => execSync(cmd, {stdio: 'inherit', cwd: root}); -// Pull polaris-tokens into the styles folder -run(`yarn run copy-polaris-tokens`); - -// Run a TypeScript build to generate type definitions (but no JS) -run(`yarn run tsc -p tsconfig.build.json`); - -// Downlevel type declarations to support consuming apps that use older versions -// of typescript -run(`yarn run downlevel-dts dist/types/latest dist/types/3.4`); - -// Run a Rollup build to generate JS and styles -run(`yarn run rollup -c`); - -// Copy documentation into the docs folder -run(`yarn run copyfiles './src/**/*.md' './dist/docs' --up=1`); - -// Copy sass files that make up our public API into the styles folder -run(`yarn run copyfiles './src/styles/**/*.scss' './dist/styles' --up=2`); +// Run Sk build to generate everything +run(`yarn run skn build`); // Run build validation tests to ensure build content is as we expect run(`node scripts/build-validate.js`); diff --git a/sewing-kit.config.ts b/sewing-kit.config.ts index 9589df26b54..e9be7270bac 100644 --- a/sewing-kit.config.ts +++ b/sewing-kit.config.ts @@ -3,14 +3,25 @@ import { Runtime, createComposedWorkspacePlugin, createComposedProjectPlugin, + createWorkspacePlugin, createProjectPlugin, + DiagnosticError, } from '@sewing-kit/core'; import {babel} from '@sewing-kit/plugin-babel'; import {workspaceTypeScript} from '@sewing-kit/plugin-typescript'; +import {packageBuild} from '@sewing-kit/plugin-package-build'; +import {rollupPlugins} from '@sewing-kit/plugin-rollup'; import {eslint} from '@sewing-kit/plugin-eslint'; import {stylelint} from '@sewing-kit/plugin-stylelint'; import {prettier} from '@sewing-kit/plugin-prettier'; import {jest} from '@sewing-kit/plugin-jest'; +import replace from '@rollup/plugin-replace'; +import image from '@rollup/plugin-image'; +import postcssShopify from '@shopify/postcss-plugin'; + +import packageJSON from './package.json'; +import {styles} from './config/rollup/plugin-styles'; +import {generateScopedName} from './config/rollup/namespaced-classname'; // eslint-disable-next-line import/no-default-export export default createPackage((pkg) => { @@ -29,6 +40,14 @@ function libraryPackagePlugin() { configFile: false, }, }), + packageBuild({ + browserTargets: 'extends @shopify/browserslist-config', + nodeTargets: 'node 12.20', + commonjs: true, + esmodules: true, + esnext: true, + }), + rollupAdjustmentsPlugin(), jestAdjustmentsPlugin(), ]); } @@ -40,6 +59,7 @@ function libaryWorkspacePlugin() { stylelint({files: '**/*.scss'}), prettier({files: '**/*.{md,json,yaml,yml}'}), workspaceTypeScript(), + preAndPostBuildPlugin(), ]); } @@ -75,3 +95,90 @@ function jestAdjustmentsPlugin() { }); }); } + +function preAndPostBuildPlugin() { + return createWorkspacePlugin('PolarisExtraBuild', ({api, tasks: {build}}) => { + build.hook(({hooks}) => { + hooks.pre.hook((steps) => [ + ...steps, + api.createStep( + {id: 'PolarisBuild.Pre', label: 'polaris pre-build'}, + async (step) => { + try { + await step.exec('yarn', ['run', 'copy-polaris-tokens'], { + all: true, + }); + } catch (error) { + throw new DiagnosticError({ + title: 'Error runing prebuild steps', + content: error.all, + }); + } + }, + ), + ]); + hooks.post.hook((steps) => [ + ...steps, + api.createStep( + {id: 'PolarisBuild.Post', label: 'polaris post-build'}, + async (step) => { + try { + await step.exec( + 'node_modules/.bin/downlevel-dts', + ['build/ts/latest', 'build/ts/3.4'], + {all: true}, + ); + + await step.exec( + 'node_modules/.bin/copyfiles', + ['./src/**/*.md', './build/docs', '--up=1'], + {all: true}, + ); + + await step.exec( + 'node_modules/.bin/copyfiles', + ['./src/styles/**/*.scss', './build/styles', '--up=2'], + {all: true}, + ); + } catch (error) { + throw new DiagnosticError({ + title: 'Error runing postbuild steps', + content: error.all, + }); + } + }, + ), + ]); + }); + }); +} + +function rollupAdjustmentsPlugin() { + return rollupPlugins((target) => { + const stylesConfig = target.options.rollupEsnext + ? { + mode: 'esnext', + modules: { + generateScopedName: generateScopedName({includeHash: true}), + }, + plugins: [postcssShopify], + } + : { + mode: 'standalone', + output: 'styles.css', + modules: { + generateScopedName: generateScopedName({includeHash: false}), + }, + plugins: [postcssShopify], + }; + + return [ + replace({ + '{{POLARIS_VERSION}}': packageJSON.version, + delimiters: ['', ''], + }), + image(), + styles(stylesConfig), + ]; + }); +} diff --git a/src/components/README.md b/src/components/README.md index 24a22a76027..6f8493178a8 100644 --- a/src/components/README.md +++ b/src/components/README.md @@ -66,7 +66,7 @@ Include the CSS in your HTML. We suggest copying the styles file into your own p ```html ``` @@ -100,7 +100,7 @@ Include the CSS stylesheet in your HTML. We suggest copying the styles file into ```html ``` diff --git a/src/utilities/custom-properties/tsconfig.json b/src/utilities/custom-properties/tsconfig.json index 1f17e634f2b..0b24d0994a6 100644 --- a/src/utilities/custom-properties/tsconfig.json +++ b/src/utilities/custom-properties/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../../tsconfig.json", "exclude": ["**/*.test.ts", "**/*.test.tsx"], "compilerOptions": { - "outDir": "../../../build", + "outDir": "../../../build-internal", "baseUrl": ".", "rootDir": "../", "module": "commonjs", diff --git a/tsconfig.build.json b/tsconfig.build.json deleted file mode 100644 index e53ed7fd97a..00000000000 --- a/tsconfig.build.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "noEmit": false, - "emitDeclarationOnly": true - }, - "include": ["./config/typescript", "./src"], - "exclude": ["..src/**/*.test.ts", "./src/**/*.test.tsx"] -} diff --git a/tsconfig.json b/tsconfig.json index 6c28da38b0c..120e2452b3c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,9 +3,10 @@ "compilerOptions": { "baseUrl": "./src", "rootDir": "./", - "noEmit": true, + "noEmit": false, + "emitDeclarationOnly": true, "incremental": true, - "outDir": "dist/types/latest", + "outDir": "build/ts/latest", "declarationMap": false, "isolatedModules": true, "importsNotUsedAsValues": "error", diff --git a/yarn.lock b/yarn.lock index 04c0c179585..6c1b0dd085f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -253,20 +253,20 @@ dependencies: "@babel/types" "^7.15.0" -"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.7.4": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.1.tgz#1644c01591a15a2f084dd6d092d9430eb1d1216c" - integrity sha512-ZeC1TlMSvikvJNy1v/wPIazCu3NdOwgYZLIkmIyAsGhqkNpiDoQQRmaCK8YP4Pq3GPTLPV9WXaPCJKvx06JxKA== - dependencies: - "@babel/types" "^7.12.1" - -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5": +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3" integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ== dependencies: "@babel/types" "^7.14.5" +"@babel/helper-module-imports@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.1.tgz#1644c01591a15a2f084dd6d092d9430eb1d1216c" + integrity sha512-ZeC1TlMSvikvJNy1v/wPIazCu3NdOwgYZLIkmIyAsGhqkNpiDoQQRmaCK8YP4Pq3GPTLPV9WXaPCJKvx06JxKA== + dependencies: + "@babel/types" "^7.12.1" + "@babel/helper-module-imports@^7.12.5": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz#1bfc0229f794988f76ed0a4d4e90860850b54dfb" @@ -2067,26 +2067,26 @@ prop-types "^15.6.1" react-lifecycles-compat "^3.0.4" -"@rollup/plugin-babel@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.1.0.tgz#ad8b5803fa6e1feb0f168984edc040b90d966450" - integrity sha512-zXBEYmfiLAMvB+ZBa6m/q9hsQYAq1sUFdjuP1F6C2pf6uQcpHwAWQveZgzS63zXdKPUYHD3Dr7BhjCqcr0bbLw== +"@rollup/plugin-babel@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz#9cb1c5146ddd6a4968ad96f209c50c62f92f9879" + integrity sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw== dependencies: - "@babel/helper-module-imports" "^7.7.4" - "@rollup/pluginutils" "^3.0.8" + "@babel/helper-module-imports" "^7.10.4" + "@rollup/pluginutils" "^3.1.0" -"@rollup/plugin-commonjs@^14.0.0": - version "14.0.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-14.0.0.tgz#4285f9ec2db686a31129e5a2b415c94aa1f836f0" - integrity sha512-+PSmD9ePwTAeU106i9FRdc+Zb3XUWyW26mo5Atr2mk82hor8+nPwkztEjFo8/B1fJKfaQDg9aM2bzQkjhi7zOw== +"@rollup/plugin-commonjs@^19.0.0": + version "19.0.2" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-19.0.2.tgz#1ccc3d63878d1bc9846f8969f09dd3b3e4ecc244" + integrity sha512-gBjarfqlC7qs0AutpRW/hrFNm+cd2/QKxhwyFa+srbg1oX7rDsEU3l+W7LAUhsAp9mPJMAkXDhLbQaVwEaE8bA== dependencies: - "@rollup/pluginutils" "^3.0.8" + "@rollup/pluginutils" "^3.1.0" commondir "^1.0.1" - estree-walker "^1.0.1" - glob "^7.1.2" - is-reference "^1.1.2" - magic-string "^0.25.2" - resolve "^1.11.0" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" "@rollup/plugin-image@^2.0.5": version "2.0.5" @@ -2096,18 +2096,17 @@ "@rollup/pluginutils" "^3.0.4" mini-svg-data-uri "^1.1.3" -"@rollup/plugin-node-resolve@^8.4.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.4.0.tgz#261d79a680e9dc3d86761c14462f24126ba83575" - integrity sha512-LFqKdRLn0ShtQyf6SBYO69bGE1upV6wUhBX0vFOUnLAyzx5cwp8svA0eHUnu8+YU57XOkrMtfG63QOpQx25pHQ== +"@rollup/plugin-node-resolve@^13.0.0": + version "13.0.4" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.4.tgz#b10222f4145a019740acb7738402130d848660c0" + integrity sha512-eYq4TFy40O8hjeDs+sIxEH/jc9lyuI2k9DM557WN6rO5OpnC2qXMBNj4IKH1oHrnAazL49C5p0tgP0/VpqJ+/w== dependencies: "@rollup/pluginutils" "^3.1.0" "@types/resolve" "1.17.1" builtin-modules "^3.1.0" - deep-freeze "^0.0.1" deepmerge "^4.2.2" is-module "^1.0.0" - resolve "^1.17.0" + resolve "^1.19.0" "@rollup/plugin-replace@^2.3.3": version "2.3.3" @@ -2182,6 +2181,18 @@ jest "^27.0.4" jest-watch-typeahead "^0.6.4" +"@sewing-kit/plugin-package-build@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@sewing-kit/plugin-package-build/-/plugin-package-build-0.5.1.tgz#eee307c29895e50fee5a553a9abc9ed7461550b0" + integrity sha512-um4mPD7tv8NZGbzgn1lsOp3CK1NuenwklRrhg4OaDuopqmYXoBpBOS93jdUlv+ScV3lD9uLPbTie8D+3yU66RA== + dependencies: + "@rollup/plugin-babel" "^5.3.0" + "@rollup/plugin-commonjs" "^19.0.0" + "@rollup/plugin-node-resolve" "^13.0.0" + "@sewing-kit/core" "^0.6.2" + "@sewing-kit/plugin-rollup" "^0.3.3" + rollup-plugin-node-externals "^2.2.0" + "@sewing-kit/plugin-prettier@^0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@sewing-kit/plugin-prettier/-/plugin-prettier-0.1.2.tgz#4fcfdc1236ea769f0418682cb319e23d824bbea3" @@ -2190,6 +2201,14 @@ "@sewing-kit/core" "^0.6.2" prettier "^2.0.0" +"@sewing-kit/plugin-rollup@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@sewing-kit/plugin-rollup/-/plugin-rollup-0.3.3.tgz#24cfd8dbd10d6ecb72e22afa9c36cd3d9d1a49bb" + integrity sha512-MUbDnmoyF6vA8POa6uasgMaOEY213jzyLExJs/2eFH1lKzGSgrvnUFI8lqaeR4sgtV/h02wnoJcbUBb4WxTSYA== + dependencies: + "@sewing-kit/core" "^0.6.2" + rollup "^2.48.0" + "@sewing-kit/plugin-stylelint@^0.4.2": version "0.4.2" resolved "https://registry.yarnpkg.com/@sewing-kit/plugin-stylelint/-/plugin-stylelint-0.4.2.tgz#1ec469188255efc1993b278232df94a65ef63248" @@ -3385,6 +3404,11 @@ "@types/cheerio" "*" "@types/react" "*" +"@types/estree@*": + version "0.0.50" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" + integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== + "@types/estree@0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" @@ -6499,11 +6523,6 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-freeze@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84" - integrity sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ= - deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -7428,6 +7447,11 @@ estree-walker@^1.0.1: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== +estree-walker@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" @@ -8089,12 +8113,7 @@ fsevents@^1.2.7: nan "^2.12.1" node-pre-gyp "^0.12.0" -fsevents@^2.1.2, fsevents@~2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" - integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== - -fsevents@^2.3.2, fsevents@~2.3.1, fsevents@~2.3.2: +fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.1, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -9428,12 +9447,12 @@ is-promise@4.0.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== -is-reference@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.1.4.tgz#3f95849886ddb70256a3e6d062b1a68c13c51427" - integrity sha512-uJA/CDPO3Tao3GTrxYn6AwkM4nUPJiGGYu5+cB8qbC7WGFlrKZbiRo7SFKxUAEpFUfiHofWCXBUNhvYJMh+6zw== +is-reference@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== dependencies: - "@types/estree" "0.0.39" + "@types/estree" "*" is-regex@^1.0.4, is-regex@^1.1.0: version "1.1.1" @@ -10661,7 +10680,7 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -magic-string@^0.25.2, magic-string@^0.25.5: +magic-string@^0.25.5, magic-string@^0.25.7: version "0.25.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== @@ -13958,7 +13977,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -14016,12 +14035,19 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^2.0.0" inherits "^2.0.1" -rollup@^2.22.2: - version "2.22.2" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.22.2.tgz#94d51fc8c30a2e545d1ff6358cc4fa14131332ee" - integrity sha512-2a4Mch4f0W2lEvkPuxtz0GfrtfgLj9bdd/oC9L3LozGOCnmLqO7ivMfKbCJoRgqWIU2UqAcbxRFSwmIKx+uStA== +rollup-plugin-node-externals@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-externals/-/rollup-plugin-node-externals-2.2.0.tgz#23946e8c0fdd0e321cc3f225d4cebc3d819d17c4" + integrity sha512-WM7TtQ76GdsLceEGmZzQzn1afj8JgOQT5VLs1Y9RMqowM/8eK2mBj/Lv7hoE833U75QsUZIRirYUtFatu51RJA== + dependencies: + find-up "^4.1.0" + +rollup@^2.48.0: + version "2.56.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.56.2.tgz#a045ff3f6af53ee009b5f5016ca3da0329e5470f" + integrity sha512-s8H00ZsRi29M2/lGdm1u8DJpJ9ML8SUOpVVBd33XNeEeL3NVaTiUcSBHzBdF3eAyR0l7VSpsuoVUGrRHq7aPwQ== optionalDependencies: - fsevents "~2.1.2" + fsevents "~2.3.2" rst-selector-parser@^2.2.3: version "2.2.3"