diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 79415f320..6dd3a11d4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ # Each line is a file pattern followed by one or more owners. # These owners will be the default owners for everything in the repo. -* @sui-bot @kikoruiz @andresz1 +* @sui-bot @kikoruiz @andresz1 @carlosvillu diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index f057e71bf..7478c36ff 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -2,74 +2,39 @@ name: CI on: push: - branches: [ master ] + branches: [master] pull_request: - branches: [ master ] - -env: - DATE: $(date +%FT%TZ) + branches: [master] jobs: build: runs-on: ubuntu-latest steps: - - name: Cancel Previous Redundant Builds - uses: styfle/cancel-workflow-action@0.6.0 - with: - access_token: ${{ github.token }} - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - persist-credentials: false - - uses: actions/setup-node@v2 - with: - node-version: 18 - registry-url: 'https://registry.npmjs.org' - - uses: browser-actions/setup-chrome@latest - - run: sudo apt-get install xvfb - - run: npm install --no-save --no-fund --no-audit --legacy-peer-deps - - run: npx -y ultra-runner --raw --recursive prepublishOnly &>/dev/null - - run: npm run lint - - run: npm run test:server:ci - - run: xvfb-run --auto-servernum npm run test:client:ci - - run: npx -p ./packages/sui-ci sui-ci release - env: - DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} - DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} - GITHUB_EMAIL: cloud-accounts@scmspain.com - GITHUB_TOKEN: ${{ secrets.GH_ACTIONS_TOKEN }} - GITHUB_USER: sui-bot - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - metrics: - name: 'Send Metrics to Devhose' - runs-on: ubuntu-latest - needs: [ build ] - if: github.ref == 'refs/heads/master' - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - persist-credentials: false - - run: | - ./bin/devhose-cli_2.17.0_amd64 deployment \ - -devhose-tenant "adevinta-spain-fe-github-sui" \ - -devhose-secret "${{ secrets.DEVHOSE_SECRET }}" \ - -devhose-dest "mpi" \ - -application "sui" \ - -environment "pro" \ - -facility "sdrn:adevinta:service:adevinta-spain-sui-github" \ - -id "${{ github.run_id }}" \ - -source "github" \ - -started-at "${{ env.DATE }}" \ - -status "succeeded" \ - -target-infrastructure "npm" \ - -target-provider "npm" \ - -trigger-type "build" \ - -trigger-source "github" \ - -resource-type "github" \ - -resource-service "sdrn:adevinta:service:github" \ - -accelerate-metrics \ - -resource-scm-commit "${{ github.sha }}" \ - -resource-repository "sdrn:adevinta:repo:github.com/${{ github.repository }}" + - name: Cancel Previous Redundant Builds + uses: styfle/cancel-workflow-action@0.6.0 + with: + access_token: ${{ github.token }} + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-node@v2 + with: + node-version: 18 + registry-url: 'https://registry.npmjs.org' + - uses: browser-actions/setup-chrome@latest + - run: sudo apt-get install xvfb + - run: npm install --no-save --no-fund --no-audit --legacy-peer-deps + - run: npx -y ultra-runner --raw --recursive prepublishOnly &>/dev/null + - run: npm run lint + - run: npm run test:server:ci + - run: xvfb-run --auto-servernum npm run test:client:ci + - run: npx -p ./packages/sui-ci sui-ci release + env: + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + GITHUB_EMAIL: cloud-accounts@scmspain.com + GITHUB_TOKEN: ${{ secrets.GH_ACTIONS_TOKEN }} + GITHUB_USER: sui-bot + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index 3a906dd8e..18fe87d62 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,5 @@ packages/sui-studio/test/server/integration/empty-studio/components/fake/compone !packages/sui-sass-loader/test/server/fixtures/**/node_modules -contract/logs/pact.log \ No newline at end of file +contract/logs/pact.log +.npmrc \ No newline at end of file diff --git a/README.md b/README.md index 21a22c5ce..2dfc8ba13 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ SUI is a set of packages which goal to ease development of SUI-based components | [sui-helpers](./packages/sui-helpers) | A set of internal helpers used by sui-related packages. | | [sui-hoc](./packages/sui-hoc) | React utility belt for function components and higher-order components | | [sui-i18n](./packages/sui-i18n) | Isomorphic i18n service for browser and node | +| [sui-jest](./packages/sui-jest) | CLI to work with Jest | | [sui-js](./packages/sui-js) | Javascript utilities | | [sui-js-compiler](./packages/sui-js-compiler) | Javascript compiler | | [sui-lint](./packages/sui-lint) | CLI to lint your code and make it compliant to SUI official rules | diff --git a/bin/devhose-cli_2.17.0_amd64 b/bin/devhose-cli_2.17.0_amd64 deleted file mode 100755 index 0bb231e2e..000000000 Binary files a/bin/devhose-cli_2.17.0_amd64 and /dev/null differ diff --git a/package.json b/package.json index fd2dde51a..b4cb63e35 100644 --- a/package.json +++ b/package.json @@ -79,4 +79,4 @@ "stylelint": { "extends": "./node_modules/@s-ui/lint/stylelint.config.js" } -} +} \ No newline at end of file diff --git a/packages/sui-bundler/CHANGELOG.md b/packages/sui-bundler/CHANGELOG.md index b080e3377..f63b674e2 100644 --- a/packages/sui-bundler/CHANGELOG.md +++ b/packages/sui-bundler/CHANGELOG.md @@ -1,5 +1,46 @@ # CHANGELOG +# 9.42.0 (2023-09-29) + + +### Features + +* **packages/sui-bundler:** Add option to save stats.json in build ([a46a346](https://github.com/SUI-Components/sui/commit/a46a346124da17fa2852d05a85a98afabf79094d)) + + + +# 9.41.0 (2023-08-28) + + +### Bug Fixes + +* **packages/sui-bundler:** Fix bundler error display ([31a1499](https://github.com/SUI-Components/sui/commit/31a1499c740709196ac08ed0cab93885f09fa711)) + + + +# 9.40.0 (2023-08-24) + + +### Features + +* **packages/sui-bundler:** disable error overlay ([2da7c44](https://github.com/SUI-Components/sui/commit/2da7c44cb79b15ab466dfef7cad34cb46bbdf2d6)) + + + +# 9.39.0 (2023-08-14) + + +### Bug Fixes + +* **packages/sui-bundler:** upgrade versions for security ([1b5d3aa](https://github.com/SUI-Components/sui/commit/1b5d3aa3659bc8f363b6ea9cf34305a04e2457c5)) + + +### Features + +* **packages/sui-bundler:** add fast refresh support ([b47bc8c](https://github.com/SUI-Components/sui/commit/b47bc8cdc2ae6c4862508b3b6c6cb39082865b08)) + + + # 9.38.0 (2023-05-04) diff --git a/packages/sui-bundler/bin/sui-bundler-build.js b/packages/sui-bundler/bin/sui-bundler-build.js index 68850e14a..da08cd73d 100755 --- a/packages/sui-bundler/bin/sui-bundler-build.js +++ b/packages/sui-bundler/bin/sui-bundler-build.js @@ -19,6 +19,7 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'production' program .option('-C, --clean', 'Remove public folder before create a new one') + .option('-S, --save-stats', 'Save stats.json in public folder') .option( '-l, --link-package [package]', 'Replace each occurrence of this package with an absolute path to this folder', @@ -43,6 +44,7 @@ program const { clean = false, context, + saveStats, linkPackage: packagesToLink = [] } = program.opts() @@ -88,6 +90,13 @@ compiler.run(async (error, stats) => { console.log(`Webpack stats: ${stats}`) + if (saveStats) { + const filePath = `${process.cwd()}/public/stats.json` + fs.writeFileSync(filePath, JSON.stringify(stats.toJson(), null, 2), { + encoding: 'utf8' + }) + } + const offlinePath = path.join(process.cwd(), 'src', 'offline.html') const offlinePageExists = fs.existsSync(offlinePath) const {offline: offlineConfig = {}} = projectConfig diff --git a/packages/sui-bundler/bin/sui-bundler-lib.js b/packages/sui-bundler/bin/sui-bundler-lib.js index 52d5693c7..a89b5ecf0 100755 --- a/packages/sui-bundler/bin/sui-bundler-lib.js +++ b/packages/sui-bundler/bin/sui-bundler-lib.js @@ -73,7 +73,18 @@ log.processing('Generating minified bundle...') webpack(webpackConfig).run((error, stats) => { if (error) { showError(error, program) - return 1 + process.exit(1) + } + + if (stats.hasErrors()) { + const jsonStats = stats.toJson('errors-warnings') + jsonStats.errors?.forEach(error => log.error(error.message)) + process.exit(1) + } + + if (stats.hasWarnings()) { + const jsonStats = stats.toJson('errors-warnings') + jsonStats.warnings?.forEach(warning => log.warn(warning.message)) } if (stats.hasErrors() || stats.hasWarnings()) { @@ -86,6 +97,4 @@ webpack(webpackConfig).run((error, stats) => { log.success( `Your library is compiled in production mode in: \n${outputFolder}` ) - - return 0 }) diff --git a/packages/sui-bundler/package.json b/packages/sui-bundler/package.json index 4fc44938e..e8a6826f8 100644 --- a/packages/sui-bundler/package.json +++ b/packages/sui-bundler/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/bundler", - "version": "9.38.0", + "version": "9.42.0", "description": "Config-free bundler for ES6 React apps.", "bin": { "sui-bundler": "./bin/sui-bundler.js" @@ -47,9 +47,11 @@ "strip-ansi": "6.0.1", "style-loader": "3.3.1", "url": "0.11.0", - "webpack": "5.74.0", + "webpack": "5.82.1", "webpack-dev-server": "4.10.0", "webpack-manifest-plugin": "5.0.0", - "webpack-node-externals": "3.0.0" + "webpack-node-externals": "3.0.0", + "@pmmmwh/react-refresh-webpack-plugin": "0.5.10", + "react-refresh": "0.14.0" } } diff --git a/packages/sui-bundler/shared/module-rules-babel.js b/packages/sui-bundler/shared/module-rules-babel.js index d3bd48f0c..1e22fea26 100644 --- a/packages/sui-bundler/shared/module-rules-babel.js +++ b/packages/sui-bundler/shared/module-rules-babel.js @@ -5,7 +5,11 @@ const EXCLUDED_FOLDERS_REGEXP = new RegExp( `node_modules(?!${path.sep}@s-ui(${path.sep}studio)(${path.sep}workbench)?${path.sep}src)` ) -module.exports = ({isServer = false, supportLegacyBrowsers = true} = {}) => ({ +module.exports = ({ + isServer = false, + isDevelopment = false, + supportLegacyBrowsers = true +} = {}) => ({ test: /\.jsx?$/, exclude: EXCLUDED_FOLDERS_REGEXP, use: [ @@ -16,6 +20,9 @@ module.exports = ({isServer = false, supportLegacyBrowsers = true} = {}) => ({ cacheCompression: false, babelrc: false, compact: true, + plugins: [ + isDevelopment && require.resolve('react-refresh/babel') + ].filter(Boolean), presets: [ [ require.resolve('babel-preset-sui'), diff --git a/packages/sui-bundler/utils/webpackHotDevClient.js b/packages/sui-bundler/utils/webpackHotDevClient.js deleted file mode 100644 index 864b51e09..000000000 --- a/packages/sui-bundler/utils/webpackHotDevClient.js +++ /dev/null @@ -1,253 +0,0 @@ -// @ts-check - -/* globals __webpack_hash__ */ - -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -'use strict' - -// This alternative WebpackDevServer combines the functionality of: -// https://github.com/webpack/webpack-dev-server/blob/webpack-1/client/index.js -// https://github.com/webpack/webpack/blob/webpack-1/hot/dev-server.js - -// It only supports their simplest configuration (hot updates on same server). -// It makes some opinionated choices on top, like adding a syntax error overlay -// that looks similar to our console output. The error overlay is inspired by: -// https://github.com/glenjamin/webpack-hot-middleware - -const stripAnsi = require('strip-ansi') -const url = require('url') -const formatWebpackMessages = require('./formatWebpackMessages.js') - -// We need to keep track of if there has been a runtime error. -// Essentially, we cannot guarantee application state was not corrupted by the -// runtime error. To prevent confusing behavior, we forcibly reload the entire -// application. This is handled below when we are notified of a compile (code -// change). -// See https://github.com/facebook/create-react-app/issues/3096 -const hadRuntimeError = false - -// Connect to WebpackDevServer via a socket. -const connection = new WebSocket( - url.format({ - protocol: window.location.protocol === 'https:' ? 'wss' : 'ws', - hostname: process.env.WDS_SOCKET_HOST || window.location.hostname, - port: process.env.WDS_SOCKET_PORT || window.location.port, - // Hardcoded in WebpackDevServer - pathname: process.env.WDS_SOCKET_PATH || '/ws', - slashes: true - }) -) - -// Unlike WebpackDevServer client, we won't try to reconnect -// to avoid spamming the console. Disconnect usually happens -// when developer stops the server. -connection.onclose = function () { - if (typeof console !== 'undefined' && typeof console.info === 'function') { - console.info( - 'The development server has disconnected.\nRefresh the page if necessary.' - ) - } -} - -// Remember some state related to hot module replacement. -let isFirstCompilation = true -let mostRecentCompilationHash = null -let hasCompileErrors = false - -function clearOutdatedErrors() { - // Clean up outdated compile errors, if any. - if (typeof console !== 'undefined' && typeof console.clear === 'function') { - if (hasCompileErrors) { - console.clear() - } - } -} - -// Successful compilation. -function handleSuccess() { - clearOutdatedErrors() - - const isHotUpdate = !isFirstCompilation - isFirstCompilation = false - hasCompileErrors = false - - // Attempt to apply hot updates or reload. - if (isHotUpdate) { - tryApplyUpdates() - } -} - -// Compilation with warnings (e.g. ESLint). -function handleWarnings(warnings) { - clearOutdatedErrors() - - const isHotUpdate = !isFirstCompilation - isFirstCompilation = false - hasCompileErrors = false - - function printWarnings() { - // Print warnings to the console. - const formatted = formatWebpackMessages({ - warnings, - errors: [] - }) - - if (typeof console !== 'undefined' && typeof console.warn === 'function') { - for (let i = 0; i < formatted.warnings.length; i++) { - if (i === 5) { - console.warn( - 'There were more warnings in other files.\n' + - 'You can find a complete log in the terminal.' - ) - break - } - console.warn(stripAnsi(formatted.warnings[i])) - } - } - } - - printWarnings() - - // Attempt to apply hot updates or reload. - if (isHotUpdate) { - tryApplyUpdates() - } -} - -// Compilation with errors (e.g. syntax error or missing modules). -function handleErrors(errors) { - clearOutdatedErrors() - - isFirstCompilation = false - hasCompileErrors = true - - // "Massage" webpack messages. - const formatted = formatWebpackMessages({ - errors, - warnings: [] - }) - - // Also log them to the console. - if (typeof console !== 'undefined' && typeof console.error === 'function') { - for (let i = 0; i < formatted.errors.length; i++) { - console.error(stripAnsi(formatted.errors[i])) - } - } - - // Do not attempt to reload now. - // We will reload on next success instead. -} - -// There is a newer version of the code available. -function handleAvailableHash(hash) { - // Update last known compilation hash. - mostRecentCompilationHash = hash -} - -// Handle messages from the server. -connection.onmessage = function (e) { - const message = JSON.parse(e.data) - switch (message.type) { - case 'hash': - handleAvailableHash(message.data) - break - case 'still-ok': - case 'ok': - handleSuccess() - break - case 'content-changed': - // Triggered when a file from `contentBase` changed. - window.location.reload() - break - case 'warnings': - handleWarnings(message.data) - break - case 'errors': - handleErrors(message.data) - break - default: - // Do nothing. - } -} - -// Is there a newer version of this code available? -function isUpdateAvailable() { - // __webpack_hash__ is the hash of the current compilation. - // It's a global variable injected by webpack. - // eslint-disable-next-line camelcase - return mostRecentCompilationHash !== __webpack_hash__ -} - -// webpack disallows updates in other states. -function canApplyUpdates() { - return module.hot.status() === 'idle' -} - -function canAcceptErrors() { - // NOTE: This var is injected by Webpack's DefinePlugin, and is a boolean instead of string. - const hasReactRefresh = process.env.FAST_REFRESH - - const status = module.hot.status() - // React refresh can handle hot-reloading over errors. - // However, when hot-reload status is abort or fail, - // it indicates the current update cannot be applied safely, - // and thus we should bail out to a forced reload for consistency. - return hasReactRefresh && ['abort', 'fail'].indexOf(status) === -1 -} - -// Attempt to update code on the fly, fall back to a hard reload. -function tryApplyUpdates(onHotUpdateSuccess) { - if (!module.hot) { - // HotModuleReplacementPlugin is not in webpack configuration. - window.location.reload() - return - } - - if (!isUpdateAvailable() || !canApplyUpdates()) { - return - } - - function handleApplyUpdates(err, updatedModules) { - const haveErrors = err || hadRuntimeError - // When there is no error but updatedModules is unavailable, - // it indicates a critical failure in hot-reloading, - // e.g. server is not ready to serve new bundle, - // and hence we need to do a forced reload. - const needsForcedReload = !err && !updatedModules - if ((haveErrors && !canAcceptErrors()) || needsForcedReload) { - window.location.reload() - return - } - - if (typeof onHotUpdateSuccess === 'function') { - // Maybe we want to do something. - onHotUpdateSuccess() - } - - if (isUpdateAvailable()) { - // While we were updating, there was a new update! Do it again. - tryApplyUpdates() - } - } - - // https://webpack.github.io/docs/hot-module-replacement.html#check - const result = module.hot.check(/* autoApply */ true, handleApplyUpdates) - - // // webpack 2 returns a Promise instead of invoking a callback - if (result && result.then) { - result.then( - function (updatedModules) { - handleApplyUpdates(null, updatedModules) - }, - function (err) { - handleApplyUpdates(err, null) - } - ) - } -} diff --git a/packages/sui-bundler/webpack.config.dev.js b/packages/sui-bundler/webpack.config.dev.js index 2c501c8f8..93b959799 100644 --- a/packages/sui-bundler/webpack.config.dev.js +++ b/packages/sui-bundler/webpack.config.dev.js @@ -3,6 +3,7 @@ const path = require('path') const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') +const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin') const { envVars, @@ -27,7 +28,6 @@ process.env.NODE_ENV = 'development' /** @typedef {import('webpack').Configuration} WebpackConfig */ -/** @type {WebpackConfig} */ const webpackConfig = { mode: 'development', context: path.resolve(PWD, 'src'), @@ -50,10 +50,13 @@ const webpackConfig = { extensions: ['.js', '.json'] }, stats: 'errors-only', - entry: cleanList([ - require.resolve('./utils/webpackHotDevClient.js'), - MAIN_ENTRY_POINT - ]), + entry: { + app: MAIN_ENTRY_POINT + }, + devServer: { + static: outputPath, + hot: true + }, target: 'web', optimization: { checkWasmTypes: false, @@ -78,12 +81,13 @@ const webpackConfig = { template: './index.html', inject: true, env: process.env - }) + }), + new ReactRefreshWebpackPlugin({overlay: false}) ], resolveLoader, module: { rules: cleanList([ - createBabelRules({supportLegacyBrowsers}), + createBabelRules({supportLegacyBrowsers, isDevelopment: true}), { test: /(\.css|\.scss)$/, use: cleanList([ diff --git a/packages/sui-i18n/CHANGELOG.md b/packages/sui-i18n/CHANGELOG.md index e846be60f..9179f5eae 100644 --- a/packages/sui-i18n/CHANGELOG.md +++ b/packages/sui-i18n/CHANGELOG.md @@ -1,5 +1,23 @@ # CHANGELOG +# 1.34.0 (2023-06-13) + + +### Bug Fixes + +* **packages/sui-i18n:** restore slash when token is validated in i18n.url ([316c987](https://github.com/SUI-Components/sui/commit/316c987e3b5c06a068e0edf493520c139b07cdfa)) + + + +# 1.33.0 (2023-06-12) + + +### Bug Fixes + +* **packages/sui-i18n:** fix missed key warning when i18n.url ([a851c59](https://github.com/SUI-Components/sui/commit/a851c59bc0ef53e466a79a45c250b46ee1a3deb6)) + + + # 1.32.0 (2023-03-03) diff --git a/packages/sui-i18n/package.json b/packages/sui-i18n/package.json index 9c0d8f5cc..7a881c98a 100644 --- a/packages/sui-i18n/package.json +++ b/packages/sui-i18n/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/i18n", - "version": "1.32.0", + "version": "1.34.0", "type": "module", "main": "lib/index.js", "description": "> Isomorphic i18n service for browser and node.", diff --git a/packages/sui-i18n/src/i18n.js b/packages/sui-i18n/src/i18n.js index 1d836b662..a81657c25 100644 --- a/packages/sui-i18n/src/i18n.js +++ b/packages/sui-i18n/src/i18n.js @@ -193,7 +193,7 @@ export default class Rosetta { url(urlPattern, allowQueryParams) { return urlPattern .split('/') - .map(token => slugify(this.t(token), allowQueryParams)) + .map(token => token && slugify(this.t(token), allowQueryParams)) .join('/') } diff --git a/packages/sui-i18n/test/fixtures/languages.js b/packages/sui-i18n/test/fixtures/languages.js index 9573291c2..a3592b431 100644 --- a/packages/sui-i18n/test/fixtures/languages.js +++ b/packages/sui-i18n/test/fixtures/languages.js @@ -1,15 +1,21 @@ export const LANGUAGES = { 'es-ES': { literalOne: 'TranslateOneEsES', - withPlural: 'uno |||| varios' + withPlural: 'uno |||| varios', + profilePathSegment: 'mi-perfil', + privacyPathSegment: 'privacidad' }, 'en-GB': { literalOne: 'TranslateOneEnGB', - withPlural: 'one |||| many' + withPlural: 'one |||| many', + profilePathSegment: 'my-profile', + privacyPathSegment: 'privacy' }, 'ca-ES': { literalOne: 'TranslateOneCaES', - withPlural: 'un |||| varis' + withPlural: 'un |||| varis', + profilePathSegment: 'meu-perfil', + privacyPathSegment: 'privacitat' } } diff --git a/packages/sui-i18n/test/i18nSpec.js b/packages/sui-i18n/test/i18nSpec.js index 0fe0643a4..cab34e50c 100644 --- a/packages/sui-i18n/test/i18nSpec.js +++ b/packages/sui-i18n/test/i18nSpec.js @@ -79,6 +79,12 @@ describe('I18N', () => { expect(i18n.n(1000000)).to.eql('1,000,000') }) + it('generate url properly', () => { + expect(i18n.url('/profilePathSegment/privacyPathSegment')).to.eql( + '/my-profile/privacy' + ) + }) + describe('with pound sterling (GBP) as currency type', () => { beforeEach(() => { i18n.currency = 'GBP' diff --git a/packages/sui-jest/.npmignore b/packages/sui-jest/.npmignore new file mode 100644 index 000000000..29693447a --- /dev/null +++ b/packages/sui-jest/.npmignore @@ -0,0 +1,2 @@ +src +test \ No newline at end of file diff --git a/packages/sui-jest/CHANGELOG.md b/packages/sui-jest/CHANGELOG.md new file mode 100644 index 000000000..e099ccf43 --- /dev/null +++ b/packages/sui-jest/CHANGELOG.md @@ -0,0 +1,11 @@ +# CHANGELOG + +# 1.1.0 (2023-06-12) + + +### Features + +* **packages/sui-jest:** add wrapper for jest ([f15be4c](https://github.com/SUI-Components/sui/commit/f15be4c48d8b1ade07a407c0781c79b181b4a52b)) + + + diff --git a/packages/sui-jest/README.md b/packages/sui-jest/README.md new file mode 100644 index 000000000..ac3f65755 --- /dev/null +++ b/packages/sui-jest/README.md @@ -0,0 +1,70 @@ +# sui-jest + +This is a CLI that abstracts away all configuration for [Jest](https://jestjs.io/). + +## Installation + +```sh +npm install -D @s-ui/jest +``` + +## Usage + +This CLI exposes a bin called `sui-jest` with all options that [Jest CLI Options](https://jestjs.io/docs/cli) supports. + +**From node** + +```sh +node ./node_modules/.bin/sui-jest [Jest CLI Options] +``` + +**From npm script** + +```json +// package.json +{ + scripts: { + "test:jest": "sui-jest", + } +} +``` +```sh +npm run test:jest -- [Jest CLI Options] +``` + +### Overriding Config + +`sui-jest` allows you to specify your own configuration. There are various ways that it works, but basically if you want to have your own config for something, just add the configuration and `sui-jest` will use it instead of its own internal config. + +**Specific** + +From CLI using the `--config` flag with the path of the specific jest config file. + +```sh +node ./node_modules/.bin/sui-jest --config jest.config.[*] +``` + +**Automatic** + +Create a `jest.config.[*]` config file in your root project or within the `packages/*`, and `sui-jest` will use the jest config file closest to where it has been executed. + +```js +// jest.config.js + +module.exports = { + testEnvironment: 'jsdom', + testMatch: ['**/__tests__/**/*.test.js'], + // ... +} +``` + +*In construction* + +In addition, `sui-jest` will expose its basic configuration so you can use it and override only the parts of the config you need to. + + +## Inspiration + +This is inspired by [kcd-scripts](https://github.com/kentcdodds/kcd-scripts). + + diff --git a/packages/sui-jest/bin/config.js b/packages/sui-jest/bin/config.js new file mode 100644 index 000000000..da7374f58 --- /dev/null +++ b/packages/sui-jest/bin/config.js @@ -0,0 +1,4 @@ +// TODO: add sui jest common config +const jestConfig = {} + +module.exports = jestConfig diff --git a/packages/sui-jest/bin/sui-jest.js b/packages/sui-jest/bin/sui-jest.js new file mode 100755 index 000000000..b5a45fd54 --- /dev/null +++ b/packages/sui-jest/bin/sui-jest.js @@ -0,0 +1,14 @@ +#!/usr/bin/env node +const {hasFile} = require('./utils.js') + +process.env.BABEL_ENV = 'test' +process.env.NODE_ENV = 'test' + +const args = process.argv.slice(2) + +const config = + !args.includes('--config') && !hasFile('jest.config.js') + ? ['--config', JSON.stringify(require('./config.js'))] + : [] + +require('jest').run([...config, ...args]) diff --git a/packages/sui-jest/bin/utils.js b/packages/sui-jest/bin/utils.js new file mode 100644 index 000000000..4d515fdda --- /dev/null +++ b/packages/sui-jest/bin/utils.js @@ -0,0 +1,19 @@ +const path = require('path') +const fs = require('fs') +const readPkgUp = require('read-pkg-up') + +const {path: pkgPath} = readPkgUp.sync({ + cwd: fs.realpathSync(process.cwd()) +}) + +const appDirectory = path.dirname(pkgPath) + +const fromRoot = (...p) => path.join(appDirectory, ...p) + +const hasFile = (...p) => fs.existsSync(fromRoot(...p)) + +module.exports = { + appDirectory, + fromRoot, + hasFile +} diff --git a/packages/sui-jest/package.json b/packages/sui-jest/package.json new file mode 100644 index 000000000..fd7cf7240 --- /dev/null +++ b/packages/sui-jest/package.json @@ -0,0 +1,29 @@ +{ + "name": "@s-ui/jest", + "version": "1.1.0", + "description": "CLI that abstracts away all configuration for Jest", + "bin": { + "sui-jest": "bin/sui-jest.js" + }, + "files": [ + "/bin" + ], + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": { + "@swc/jest": "0.2.24", + "@types/jest": "29.2.4", + "jest": "29.3.1", + "jest-environment-jsdom": "29.3.1", + "read-pkg-up": "7.0.1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/SUI-Components/sui.git" + }, + "bugs": { + "url": "https://github.com/SUI-Components/sui/issues" + }, + "homepage": "https://github.com/SUI-Components/sui/tree/master/packages/sui-jest#readme" +} diff --git a/packages/sui-js-compiler/.gitignore b/packages/sui-js-compiler/.gitignore new file mode 100644 index 000000000..37e9ae0be --- /dev/null +++ b/packages/sui-js-compiler/.gitignore @@ -0,0 +1 @@ +test/server/tsconfig.json diff --git a/packages/sui-js-compiler/CHANGELOG.md b/packages/sui-js-compiler/CHANGELOG.md index 4739b05b9..ade38581a 100644 --- a/packages/sui-js-compiler/CHANGELOG.md +++ b/packages/sui-js-compiler/CHANGELOG.md @@ -1,5 +1,28 @@ # CHANGELOG +# 1.25.0 (2023-06-28) + + +### Bug Fixes + +* **packages/sui-js-compiler:** fix linting issues ([9ae715c](https://github.com/SUI-Components/sui/commit/9ae715cbdbb1ae5f55f727cdf5f01a8250ba6b91)) + + +### Features + +* **packages/sui-js-compiler:** install typescript on demand ([432ffc3](https://github.com/SUI-Components/sui/commit/432ffc3ce08423da1b0e924fad5d1bbe72c8b6c7)) + + + +# 1.24.0 (2023-06-12) + + +### Bug Fixes + +* **packages/sui-js-compiler:** hide ts import on non ts compilations ([5c28421](https://github.com/SUI-Components/sui/commit/5c284210db121ba195b56f85e80736b14b196b54)) + + + # 1.23.0 (2023-05-25) @@ -25,8 +48,10 @@ ### Features * **packages/sui-js-compiler:** add legacy option ([b4d234e](https://github.com/SUI-Components/sui/commit/b4d234e980492de24d6bb30450c7586ab64a9cc0)) +* **packages/sui-js-compiler:** add new test folder to ignore ([113e481](https://github.com/SUI-Components/sui/commit/113e4810e32f6e8a32869489dce966907e932fa8)) * **packages/sui-js-compiler:** change to modern option ([86101a6](https://github.com/SUI-Components/sui/commit/86101a63560f3f818f9f64f8821ad48933360db4)) * **packages/sui-js-compiler:** set typescript support from tsconfig ([78a155c](https://github.com/SUI-Components/sui/commit/78a155cdde96f52b5bec9c1ebb1c11469a7f2923)) +* **packages/sui-js-compiler:** use ignore name for destructuring and new test folder ([8e4769e](https://github.com/SUI-Components/sui/commit/8e4769ef056e64c651b14aca35aa4682d04064cd)) diff --git a/packages/sui-js-compiler/index.js b/packages/sui-js-compiler/index.js index cef7fed9e..d81b6f13d 100755 --- a/packages/sui-js-compiler/index.js +++ b/packages/sui-js-compiler/index.js @@ -7,10 +7,11 @@ import program from 'commander' import fg from 'fast-glob' import fs from 'fs-extra' import path from 'node:path' -import ts from 'typescript' import {transformFile} from '@swc/core' +import {getSpawnPromise} from '@s-ui/helpers/cli.js' + import {getSWCConfig} from './swc-config.js' const SOURCE_DIR = './src' @@ -33,7 +34,21 @@ const DEFAULT_TS_CONFIG = { types: ['react', 'node'] } -const getTSConfig = () => { +const dynamicPackage = async (name, {version} = {}) => { + const packageName = version ? `${name}@${version}` : name + + try { + await getSpawnPromise('npm', ['explain', packageName]) + } catch (error) { + if (error.exitCode === 1) { + await getSpawnPromise('npm', ['install', packageName, '--no-save']) + } + } + + return import(packageName).then(module => module.default) +} + +const getTsConfig = () => { // Get TS config from the package dir. const tsConfigPath = path.join(process.cwd(), 'tsconfig.json') let tsConfig @@ -55,21 +70,22 @@ const compileFile = async (file, options) => { .replace(SOURCE_DIR, OUTPUT_DIR) .replace(TS_EXTENSION_REGEX, COMPILED_EXTENSION) - fs.outputFile(outputPath, code) + await fs.outputFile(outputPath, code) } -const compileTypes = (files, options) => { +const compileTypes = async (files, options) => { + const {createCompilerHost, createProgram} = await dynamicPackage('typescript') const createdFiles = {} - const host = ts.createCompilerHost(options) + const host = createCompilerHost(options) host.writeFile = (fileName, contents) => (createdFiles[fileName] = contents) - const program = ts.createProgram(files, options, host) + const program = createProgram(files, options, host) program.emit() return Promise.all( - Object.keys(createdFiles).map(outputPath => { + Object.keys(createdFiles).map(async outputPath => { const code = createdFiles[outputPath] - return fs.outputFile(outputPath, code) + await fs.outputFile(outputPath, code) }) ) } @@ -94,7 +110,8 @@ program }) .parse(process.argv) -const {ignore = [], modern: isModern = false} = program.opts() +const {ignore: ignoreOpts = [], modern: isModern = false} = program.opts() +const ignore = [...ignoreOpts, '**/__tests__'] ;(async () => { console.time('[sui-js-compiler]') @@ -104,14 +121,14 @@ const {ignore = [], modern: isModern = false} = program.opts() files.map(async file => { const isTypeScript = Boolean(file.match(TS_EXTENSION_REGEX)) - return compileFile(file, {isModern, isTypeScript}) + await compileFile(file, {isModern, isTypeScript}) }) ) - const tsConfig = getTSConfig() + const tsConfig = getTsConfig() // If TS config exists, set TypeScript as enabled. const isTypeScriptEnabled = Boolean(tsConfig) const typesToCompile = isTypeScriptEnabled - ? compileTypes(files, { + ? await compileTypes(files, { ...DEFAULT_TS_CONFIG, ...(tsConfig?.compilerOptions ?? {}) }) diff --git a/packages/sui-js-compiler/package.json b/packages/sui-js-compiler/package.json index cba7bf2c5..4bb9089dc 100644 --- a/packages/sui-js-compiler/package.json +++ b/packages/sui-js-compiler/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/js-compiler", - "version": "1.23.0", + "version": "1.25.0", "description": "JavaScript & TypeScript Compiler", "type": "module", "exports": "./src/index.js", @@ -12,11 +12,11 @@ }, "license": "MIT", "dependencies": { + "@s-ui/helpers": "1", "@swc/core": "1.3.14", "@swc/helpers": "0.4.12", "commander": "8.3.0", "fast-glob": "3.2.12", - "fs-extra": "10.1.0", - "typescript": "5.0.4" + "fs-extra": "10.1.0" } } diff --git a/packages/sui-js-compiler/test/server/index.js b/packages/sui-js-compiler/test/server/jsCompilerSpec.js similarity index 54% rename from packages/sui-js-compiler/test/server/index.js rename to packages/sui-js-compiler/test/server/jsCompilerSpec.js index 513fb2efe..bca71bfae 100644 --- a/packages/sui-js-compiler/test/server/index.js +++ b/packages/sui-js-compiler/test/server/jsCompilerSpec.js @@ -6,17 +6,27 @@ import {exec as execCallback} from 'node:child_process' import {join} from 'node:path' import {promisify} from 'node:util' +const DEFAULT_TIMEOUT = 9000 + const exec = promisify(execCallback) const cwd = fileURLToPath(new URL('.', import.meta.url)) const libPath = fileURLToPath(new URL('lib', import.meta.url)) +const tsConfigPath = fileURLToPath(new URL('tsconfig.json', import.meta.url)) const libFilePath = join(libPath, 'example.js') describe('@s-ui/js-compiler', () => { - beforeEach(() => fs.remove(libPath)) - afterEach(() => fs.remove(libPath)) + beforeEach(async () => { + await fs.remove(libPath) + await fs.remove(tsConfigPath) + }) + + afterEach(async () => { + await fs.remove(libPath) + await fs.remove(tsConfigPath) + }) - it('compiles a /src folder with a JavaScript with JSX file and output to /lib', async () => { + it('should compile a "src" folder with a JavaScript file and output it to "lib"', async () => { const {stdout} = await exec('node ../../index.js', { cwd }) @@ -35,9 +45,9 @@ describe('@s-ui/js-compiler', () => { expect(compiledFile).to.contain('_async_to_generator') expect(compiledFile).to.contain('_ts_decorate') expect(compiledFile).to.contain('_ts_generator') - }) + }).timeout(DEFAULT_TIMEOUT) - it('when the "ignore" option exists, it exclude all the file matching the passed patterns', async () => { + it('should exclude all the files matching the passed patterns when the "ignore" option exists', async () => { const {stdout} = await exec( 'node ../../index.js --ignore="./src/**.test.js"', { @@ -59,5 +69,33 @@ describe('@s-ui/js-compiler', () => { expect(compiledFile).to.contain('_async_to_generator') expect(compiledFile).to.contain('_ts_decorate') expect(compiledFile).to.contain('_ts_generator') - }) + }).timeout(DEFAULT_TIMEOUT) + + it('should compile a "src" folder with a JSX file written in TypeScript and output it to "lib"', async () => { + // GIVEN a "tsconfig.json" definition in the package root directory + const tsConfig = { + extends: '../../../../tsconfig.json', + compilerOptions: { + outDir: './lib', + rootDir: './src' + }, + exclude: ['node_modules', 'lib'] + } + + await fs.outputFile(tsConfigPath, JSON.stringify(tsConfig)) + + // WHEN execute the compiler command + const {stdout} = await exec('node ../../index.js', { + cwd + }) + const compiledFilenames = await fs.readdir(libPath) + + // THEN package files and types are properly compiled + expect(stdout).to.contain('[sui-js-compiler]') + expect(compiledFilenames).to.eql([ + 'example.d.ts', + 'example.js', + 'example.test.js' + ]) + }).timeout(DEFAULT_TIMEOUT) }) diff --git a/packages/sui-js-compiler/test/server/src/__tests__/other-example.test.js b/packages/sui-js-compiler/test/server/src/__tests__/other-example.test.js new file mode 100644 index 000000000..466d67930 --- /dev/null +++ b/packages/sui-js-compiler/test/server/src/__tests__/other-example.test.js @@ -0,0 +1 @@ +// File to ignore diff --git a/packages/sui-js-compiler/test/server/src/example.tsx b/packages/sui-js-compiler/test/server/src/example.tsx new file mode 100644 index 000000000..d7e209472 --- /dev/null +++ b/packages/sui-js-compiler/test/server/src/example.tsx @@ -0,0 +1,9 @@ +/* eslint-disable */ +interface ThingProps { + name: string + type?: 'inert' | 'moving' +} + +export default function Thing({name, type = 'moving'}: ThingProps): React.FC { + return
{name}
+} diff --git a/packages/sui-js/CHANGELOG.md b/packages/sui-js/CHANGELOG.md index 3e842ba9f..dc0147e72 100644 --- a/packages/sui-js/CHANGELOG.md +++ b/packages/sui-js/CHANGELOG.md @@ -1,5 +1,23 @@ # CHANGELOG +# 2.28.0 (2023-08-16) + + +### Bug Fixes + +* **packages/sui-js:** upgrade qs version for security ([3eb8343](https://github.com/SUI-Components/sui/commit/3eb8343db512d053b0fa7066a36dd591505d2767)) + + + +# 2.27.0 (2023-08-14) + + +### Bug Fixes + +* **packages/sui-js:** upgrade versions for security ([f6877b6](https://github.com/SUI-Components/sui/commit/f6877b6c7cc4db3ca84f69f15f4bef8a3517d957)) + + + # 2.26.0 (2022-06-28) diff --git a/packages/sui-js/package.json b/packages/sui-js/package.json index f35b5a2b1..7a32d0024 100644 --- a/packages/sui-js/package.json +++ b/packages/sui-js/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/js", - "version": "2.26.0", + "version": "2.28.0", "description": "Set of JS utilities", "type": "module", "scripts": { @@ -20,7 +20,7 @@ "lodash.debounce": "4.0.8", "lodash.throttle": "4.1.1", "nanoid": "3.3.1", - "qs": "6.7.0", + "qs": "6.11.2", "remove-accents": "0.4.2" }, "license": "MIT", diff --git a/packages/sui-lint/CHANGELOG.md b/packages/sui-lint/CHANGELOG.md index 83bf3ccb2..7c2270d9d 100644 --- a/packages/sui-lint/CHANGELOG.md +++ b/packages/sui-lint/CHANGELOG.md @@ -1,5 +1,28 @@ # CHANGELOG +# 4.33.0 (2023-06-13) + + +### Bug Fixes + +* **packages/sui-lint:** update jest rules ([eb9cc8d](https://github.com/SUI-Components/sui/commit/eb9cc8d17b350b6fed55f81fc989309988d78551)) + + + +# 4.32.0 (2023-06-13) + + + +# 4.31.0 (2023-06-12) + + +### Features + +* **packages/sui-lint:** add jest rules ([0e2f2a2](https://github.com/SUI-Components/sui/commit/0e2f2a2e9274c7bb7d5e875c5ee029dc0460743f)) +* **packages/sui-lint:** remove comment ([df148e5](https://github.com/SUI-Components/sui/commit/df148e5f86429baa424d5a082937b2e53fe0e5b9)) + + + # 4.30.0 (2023-05-18) @@ -9,6 +32,11 @@ * **packages/sui-lint:** use version 8 for stylelint-config-recommended-scss ([60a1179](https://github.com/SUI-Components/sui/commit/60a1179effcc998e9a5c7fb1ca5b88b1e97fd7bb)) +### Features + +* **packages/sui-lint:** add jest rules ([3e3dcaa](https://github.com/SUI-Components/sui/commit/3e3dcaa24a926be7c462dcb9fcdb00de84c43dce)) + + # 4.29.0 (2022-12-29) diff --git a/packages/sui-lint/eslintrc.js b/packages/sui-lint/eslintrc.js index 57d2167c7..bf62bca3b 100644 --- a/packages/sui-lint/eslintrc.js +++ b/packages/sui-lint/eslintrc.js @@ -44,6 +44,71 @@ const TESTING_RULES = { 'no-only-tests/no-only-tests': RULES.ERROR } +const JEST_TESTING_RULES = { + 'react/display-name': RULES.OFF, + 'jest/consistent-test-it': RULES.OFF, + 'jest/expect-expect': RULES.OFF, + 'jest/max-expects': RULES.OFF, + 'jest/max-nested-describe': RULES.ERROR, + 'jest/no-alias-methods': RULES.OFF, + 'jest/no-commented-out-tests': RULES.WARNING, + 'jest/no-conditional-expect': RULES.ERROR, + 'jest/no-conditional-in-test': RULES.ERROR, + 'jest/no-deprecated-functions': RULES.ERROR, + 'jest/no-disabled-tests': RULES.WARNING, + 'jest/no-done-callback': RULES.ERROR, + 'jest/no-duplicate-hooks': RULES.OFF, + 'jest/no-export': RULES.ERROR, + 'jest/no-focused-tests': RULES.ERROR, + 'jest/no-hooks': RULES.OFF, + 'jest/no-identical-title': RULES.ERROR, + 'jest/no-if': RULES.ERROR, + 'jest/no-interpolation-in-snapshots': RULES.ERROR, + 'jest/no-jasmine-globals': RULES.OFF, + 'jest/no-large-snapshots': [RULES.WARNING, {maxSize: 300}], + 'jest/no-mocks-import': RULES.ERROR, + 'jest/no-restricted-matchers': RULES.OFF, + 'jest/no-standalone-expect': RULES.OFF, + 'jest/no-test-prefixes': RULES.ERROR, + 'jest/no-test-return-statement': RULES.OFF, + 'jest/prefer-called-with': RULES.ERROR, + 'jest/prefer-comparison-matcher': RULES.ERROR, + 'jest/prefer-each': RULES.ERROR, + 'jest/prefer-equality-matcher': RULES.ERROR, + 'jest/prefer-expect-assertions': RULES.OFF, + 'jest/prefer-expect-resolves': RULES.OFF, + 'jest/prefer-hooks-in-order': RULES.ERROR, + 'jest/prefer-hooks-on-top': RULES.ERROR, + 'jest/prefer-lowercase-title': RULES.OFF, + 'jest/prefer-mock-promise-shorthand': RULES.ERROR, + 'jest/prefer-snapshot-hint': RULES.ERROR, + 'jest/prefer-spy-on': RULES.OFF, + 'jest/prefer-strict-equal': RULES.OFF, + 'jest/prefer-to-be': RULES.OFF, + 'jest/prefer-to-contain': RULES.WARNING, + 'jest/prefer-to-have-length': RULES.WARNING, + 'jest/prefer-todo': RULES.WARNING, + 'jest/require-hook': RULES.OFF, + 'jest/require-to-throw-message': RULES.OFF, + 'jest/require-top-level-describe': RULES.OFF, + 'jest/unbound-method': RULES.OFF, + 'jest/valid-describe-callback': RULES.ERROR, + 'jest/valid-expect': RULES.ERROR, + 'jest/valid-expect-in-promise': RULES.ERROR, + 'jest/valid-title': RULES.WARNING, + 'jest-dom/prefer-checked': RULES.ERROR, + 'jest-dom/prefer-empty': RULES.ERROR, + 'jest-dom/prefer-enabled-disabled': RULES.ERROR, + 'jest-dom/prefer-focus': RULES.ERROR, + 'jest-dom/prefer-in-document': RULES.ERROR, + 'jest-dom/prefer-required': RULES.ERROR, + 'jest-dom/prefer-to-have-attribute': RULES.ERROR, + 'jest-dom/prefer-to-have-class': RULES.ERROR, + 'jest-dom/prefer-to-have-style': RULES.ERROR, + 'jest-dom/prefer-to-have-text-content': RULES.ERROR, + 'jest-dom/prefer-to-have-value': RULES.ERROR +} + const IMPORT_SORT_GROUPS = [ // Side effect and polyfill imports. ['^\\u0000'], @@ -83,7 +148,8 @@ module.exports = { env: { es6: true, - mocha: true + mocha: true, + 'jest/globals': true }, globals: { @@ -116,7 +182,9 @@ module.exports = { 'prettier', 'react', 'react-hooks', - 'simple-import-sort' + 'simple-import-sort', + 'jest', + 'jest-dom' ], rules: { ...REACT_RULES, @@ -156,6 +224,10 @@ module.exports = { 'prettier/prettier': RULES.OFF, 'react/react-in-jsx-scope': RULES.OFF } + }, + { + files: ['**/__tests__/**/*.js'], + rules: JEST_TESTING_RULES } ] } diff --git a/packages/sui-lint/package.json b/packages/sui-lint/package.json index e7fc91d15..3023e77a1 100644 --- a/packages/sui-lint/package.json +++ b/packages/sui-lint/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/lint", - "version": "4.30.0", + "version": "4.33.0", "description": "Linting CLI for sui packages", "main": "./bin/sui-lint.js", "bin": { @@ -27,12 +27,14 @@ "eslint-plugin-chai-friendly": "0.7.2", "eslint-plugin-cypress": "2.12.1", "eslint-plugin-import": "2.26.0", - "eslint-plugin-no-only-tests": "3.0.0", + "eslint-plugin-jest": "27.2.1", + "eslint-plugin-jest-dom": "4.0.3", "eslint-plugin-n": "15.2.5", + "eslint-plugin-no-only-tests": "3.0.0", "eslint-plugin-prettier": "4.2.1", "eslint-plugin-promise": "6.0.0", - "eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-react": "7.30.1", + "eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-simple-import-sort": "7.0.0", "postcss-scss": "4.0.4", "prettier": "2.7.1", diff --git a/packages/sui-mono/CHANGELOG.md b/packages/sui-mono/CHANGELOG.md index 794d94442..6e4526d56 100644 --- a/packages/sui-mono/CHANGELOG.md +++ b/packages/sui-mono/CHANGELOG.md @@ -1,5 +1,76 @@ # CHANGELOG +# 2.41.0 (2023-10-09) + + +### Features + +* **packages/sui-mono:** avoid update lock file if no package is updated ([daab85a](https://github.com/SUI-Components/sui/commit/daab85a857b82fd0ae6b468910f1f93248372b5f)) + + + +# 2.40.0 (2023-10-06) + + +### Features + +* **packages/sui-mono:** add lockfile support ([51405ef](https://github.com/SUI-Components/sui/commit/51405ef74149d06b974bbb1283497d7b1f9dd78a)) +* **packages/sui-mono:** commit lockfile ([e7e93be](https://github.com/SUI-Components/sui/commit/e7e93be91982952d746d6510f98d4e681c6134a3)) +* **packages/sui-mono:** update lint issues ([0ee3dc7](https://github.com/SUI-Components/sui/commit/0ee3dc7c1bb95fc1611c55bd05849ab6f8505906)) +* **packages/sui-mono:** update lockfile conditionally ([d34c8c7](https://github.com/SUI-Components/sui/commit/d34c8c7cf10f825450f96f02d83267daf72cb17a)) + + + +# 2.39.0 (2023-09-07) + + +### Bug Fixes + +* **packages/sui-mono:** remove --no-verify flag ([9ca7299](https://github.com/SUI-Components/sui/commit/9ca72990c8c476a326e6b64fa9206c3e24880ec9)) + + +### Features + +* **packages/sui-mono:** add no-verify flag to publish npm packages ([12ae25f](https://github.com/SUI-Components/sui/commit/12ae25f804146653b55ebfad645502d5e2806be4)) + + + +# 2.38.0 (2023-08-22) + + +### Features + +* **packages/sui-mono:** add overrides config support ([8c774ef](https://github.com/SUI-Components/sui/commit/8c774ef35550c6a501be666bc1a0f216bc9deb74)) + + + +# 2.37.0 (2023-08-17) + + +### Features + +* **packages/sui-mono:** update gir-url-parse version ([dd0c681](https://github.com/SUI-Components/sui/commit/dd0c681f5935354b1eaba6317f591dffa15dfec6)) + + + +# 2.36.0 (2023-08-14) + + +### Bug Fixes + +* **packages/sui-mono:** upgrade versions for security ([5ce05a6](https://github.com/SUI-Components/sui/commit/5ce05a6f233b2ad97a164ade36e8702aed9fd8b7)) + + + +# 2.35.0 (2023-06-09) + + +### Features + +* **packages/sui-mono:** upgrade git-url-parse dependency to major 13 ([bf53091](https://github.com/SUI-Components/sui/commit/bf530911331d5f17c573cd9a24e195191176f3eb)) + + + # 2.34.0 (2022-10-05) diff --git a/packages/sui-mono/README.md b/packages/sui-mono/README.md index 77ffa6ca8..18de6b785 100644 --- a/packages/sui-mono/README.md +++ b/packages/sui-mono/README.md @@ -28,14 +28,15 @@ We use: - [`phoenix`](#phoenix) - [`commit`](#commit) - [`commit-all` commit for all contained packages](#commit-all-commit-for-all-contained-packages) - - [`check` & `release`](#check--release) + - [`check` \& `release`](#check--release) - [How to configure your project](#how-to-configure-your-project) - [`private`](#private) - [`access`](#access) - [`workspaces`](#workspaces) + - [`overrides`](#overrides) - [Examples](#examples) - - [Project Example](#project-example) - - [Case `sui-studio`](#case-sui-studio) + - [Project Example](#project-example) + - [Case `sui-studio`](#case-sui-studio) - [Manual scopes](#manual-scopes) - [Migration from v1](#migration-from-v1) - [`packagesFolder` and `deepLevel` are not longer used](#packagesfolder-and-deeplevel-are-not-longer-used) @@ -165,6 +166,12 @@ In case you want to release via CI the `--github-user` `--github-email` and `--g sui-mono release --github-user [username] --github-email [user email] --github-token [TOKEN] ``` +If you want the `package-lock.json` to be committed once the packages are released, use the --lock flag. + +```sh +sui-mono release --lock +``` + ## How to configure your project First you need to install the `@s-ui/mono` package in your project @@ -185,6 +192,11 @@ Here's a full example of the options: "config": { "sui-mono": { "access": "public", + "overrides": { + "literals": [{ + "regex": "Lokalise:" + }] + } }, "validate-commit-msg": { "types": "@s-ui/mono/src/types" @@ -221,6 +233,27 @@ These `workspaces` are defined in the root `package.json` of the project and use This information will be used for releases and [`commitizen`](https://commitizen.github.io/cz-cli/) scopes. +### `overrides` + +There are some cases when the commit message is automatically generated by a third-party (e.g. Lokalise). For this kind of case the `overrides` field could be used. + +```json +"overrides": { + "literals": [{ + "regex": "Lokalise:" + }, { + "regex": "Update lokalise" + }], + "domain": [{ + "regex": "Tracker" + }] +} +``` + +The overrides field takes an object of scopes, where each scope can have a collection of regular expressions defined under the literals key. When the commit header matches any of the specified regexes, a minor package update will be performed. + +This feature grants you the flexibility to seamlessly manage automatic commit messages from external sources, maintaining versioning accuracy. + #### Examples ###### Project Example @@ -237,7 +270,7 @@ src/ You will have to add the next config on `workspaces` in your `package.json` root file: -```json +````json { "workspaces": ["src/**"] } @@ -248,7 +281,7 @@ This will give you a list of scopes like this one for your commit: src/i18n src/users src/search -``` +```` ###### Case `sui-studio` diff --git a/packages/sui-mono/bin/sui-mono-release.js b/packages/sui-mono/bin/sui-mono-release.js index 2a6614863..a499574c6 100644 --- a/packages/sui-mono/bin/sui-mono-release.js +++ b/packages/sui-mono/bin/sui-mono-release.js @@ -19,6 +19,7 @@ program .option('-T, --github-token ', 'github token') .option('-U, --github-user ', 'github user') .option('-E, --github-email ', 'github email') + .option('-L, --lock', 'Commit lock file', false) .option('--skip-ci', 'Add [skip ci] to release commit message', false) .on('--help', () => { console.log(' Description:') @@ -49,6 +50,7 @@ const { githubEmail, githubToken, githubUser, + lock, skipCi } = program.opts() @@ -93,7 +95,6 @@ const releasePackage = async ({pkg, code, skipCi} = {}) => { // https://docs.travis-ci.com/user/customizing-the-build/#skipping-a-build const skipCiSuffix = skipCi ? ' [skip ci]' : '' const commitMsg = `release(${packageScope}): v${version}${skipCiSuffix}` - await exec(`git commit -m "${commitMsg}"`, {cwd}) await exec(`${suiMonoBinPath} changelog ${cwd}`, {cwd}) @@ -106,8 +107,6 @@ const releasePackage = async ({pkg, code, skipCi} = {}) => { const publishAccess = getPublishAccess({localPackageConfig}) await exec(`npm publish --access=${publishAccess}`, {cwd}) } - - await exec('git push -f --tags origin HEAD') } const checkIsMasterBranchActive = async () => { @@ -181,6 +180,20 @@ checkShouldRelease() await releasePackage({...pkg, skipCi}) } + if (packagesToRelease.length > 0) { + if (lock) { + await exec( + 'npm install --package-lock-only --legacy-peer-deps --no-audit --no-fund --ignore-scripts --production=false' + ) + await exec('git add package-lock.json') + await exec( + 'git commit -m "chore(Root): update package-lock.json [skip ci]" --no-verify' + ) + } + + await exec('git push -f --tags origin HEAD') + } + console.log( `[sui-mono release] ${packagesToRelease.length} packages released` ) diff --git a/packages/sui-mono/package.json b/packages/sui-mono/package.json index b842ba482..a61d41411 100644 --- a/packages/sui-mono/package.json +++ b/packages/sui-mono/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/mono", - "version": "2.34.0", + "version": "2.41.0", "description": "Commit and release manager", "main": "index.js", "bin": { @@ -12,9 +12,9 @@ "commander": "8.3.0", "conventional-changelog": "3.1.25", "enquirer": "2.3.6", - "git-url-parse": "12.0.0", + "git-url-parse": "13.1.0", "glob": "8.0.3", - "word-wrap": "1.2.3" + "word-wrap": "1.2.4" }, "repository": { "type": "git", diff --git a/packages/sui-mono/src/check.js b/packages/sui-mono/src/check.js index a75b6dd16..cb1715e27 100644 --- a/packages/sui-mono/src/check.js +++ b/packages/sui-mono/src/check.js @@ -6,7 +6,8 @@ const {readJsonSync} = require('fs-extra') const { checkIsMonoPackage, getProjectName, - getWorkspaces + getWorkspaces, + getOverrides } = require('./config.js') const gitRawCommitsOpts = {reverse: true, topoOrder: true} @@ -48,6 +49,58 @@ const flatten = status => const getPkgFromScope = scope => (scope === 'Root' ? '.' : scope) +const getOverride = ({overrides, header}) => { + return Object.entries(overrides).find(([, configs]) => { + return configs.find(({regex}) => header.match(new RegExp(regex))) + }) +} + +const getTransform = + ({status, packages, overrides = getOverrides()} = {}) => + (commit, cb) => { + const {scope, header} = commit + const [pkgToOverride] = getOverride({overrides, header}) ?? [] + const pkg = pkgToOverride ?? getPkgFromScope(scope) + + let toPush = null + + if (!packages.includes(pkg)) return cb() + + if (pkgToOverride) { + status[pkgToOverride].increment = Math.max( + status[pkgToOverride].increment, + PACKAGE_VERSION_INCREMENT.MINOR + ) + toPush = commit + } + + if (isCommitReleaseTrigger(commit)) { + status[pkg].increment = Math.max( + status[pkg].increment, + PACKAGE_VERSION_INCREMENT.MINOR + ) + toPush = commit + } + + if (isCommitBreakingChange(commit)) { + status[pkg].increment = Math.max( + status[pkg].increment, + PACKAGE_VERSION_INCREMENT.MAJOR + ) + toPush = commit + } + + if (toPush) { + status[pkg].commits.push(commit) + } + + if (commit.type === 'release') { + status[pkg].increment = PACKAGE_VERSION_INCREMENT.NOTHING + status[pkg].commits = [] + } + cb() + } + const check = () => new Promise(resolve => { /** @@ -71,37 +124,7 @@ const check = () => { preset: 'angular', append: true, - transform: (commit, cb) => { - const pkg = getPkgFromScope(commit.scope) - - if (!packagesWithChangelog.includes(pkg)) return cb() - - let toPush = null - - if (isCommitReleaseTrigger(commit)) { - status[pkg].increment = Math.max( - status[pkg].increment, - PACKAGE_VERSION_INCREMENT.MINOR - ) - toPush = commit - } - - if (isCommitBreakingChange(commit)) { - status[pkg].increment = Math.max( - status[pkg].increment, - PACKAGE_VERSION_INCREMENT.MAJOR - ) - toPush = commit - } - if (toPush) { - status[pkg].commits.push(commit) - } - if (commit.type === 'release') { - status[pkg].increment = PACKAGE_VERSION_INCREMENT.NOTHING - status[pkg].commits = [] - } - cb() - } + transform: getTransform({status, packages: packagesWithChangelog}) }, {}, gitRawCommitsOpts @@ -114,6 +137,7 @@ const check = () => module.exports = { check, + getTransform, isCommitBreakingChange, isCommitReleaseTrigger } diff --git a/packages/sui-mono/src/config.js b/packages/sui-mono/src/config.js index 35fbdb9f4..924e608d2 100644 --- a/packages/sui-mono/src/config.js +++ b/packages/sui-mono/src/config.js @@ -45,6 +45,9 @@ function factoryConfigMethods(packageFile) { getPublishAccess: ({localPackageConfig} = {}) => { return getPublishAccess({localPackageConfig, packageConfig}) }, + getOverrides: () => { + return packageConfig['sui-mono']?.overrides ?? {} + }, getWorkspaces: () => getWorkspaces(workspaces) } } diff --git a/packages/sui-mono/test/server/checkSpec.js b/packages/sui-mono/test/server/checkSpec.js index 605c84f63..555dee45b 100644 --- a/packages/sui-mono/test/server/checkSpec.js +++ b/packages/sui-mono/test/server/checkSpec.js @@ -1,6 +1,7 @@ import {expect} from 'chai' import { + getTransform, isCommitBreakingChange, isCommitReleaseTrigger } from '../../src/check.js' @@ -42,5 +43,92 @@ describe('check', () => { }) }) - describe('getAllTaskArrays', () => {}) + describe('getTransform', () => { + it('should not increment status when commit is not valid', async () => { + const status = { + literals: { + increment: 0, + commits: [] + } + } + const packages = ['literals'] + const overrides = {} + + const transform = getTransform({status, packages, overrides}) + const commit = { + type: 'Lokalise', + scope: null, + header: 'Lokalise: updates' + } + + await new Promise(resolve => transform(commit, resolve)) + + expect(status).to.be.deep.equal({ + literals: { + increment: 0, + commits: [] + } + }) + }) + + it('should increment status when commit is valid', async () => { + const status = { + literals: { + increment: 0, + commits: [] + } + } + const packages = ['literals'] + const overrides = {} + + const transform = getTransform({status, packages, overrides}) + const commit = { + type: 'feat', + scope: 'literals', + header: 'updates' + } + + await new Promise(resolve => transform(commit, resolve)) + + expect(status).to.be.deep.equal({ + literals: { + increment: 2, + commits: [commit] + } + }) + }) + + it('should increment status when commit is not valid but is overrided', async () => { + const status = { + literals: { + increment: 0, + commits: [] + } + } + const packages = ['literals'] + const overrides = { + literals: [ + { + regex: 'Lokalise:' + } + ] + } + + const transform = getTransform({status, packages, overrides}) + const commit = { + type: 'Lokalise', + scope: null, + header: 'Lokalise: updates' + } + + await new Promise(resolve => transform(commit, resolve)) + + expect(status).to.be.deep.equal({ + literals: { + increment: 2, + commits: [commit] + } + }) + }) + }) }) diff --git a/packages/sui-mono/test/server/configSpec.js b/packages/sui-mono/test/server/configSpec.js index 5aefaa1f9..c8768c329 100644 --- a/packages/sui-mono/test/server/configSpec.js +++ b/packages/sui-mono/test/server/configSpec.js @@ -112,4 +112,31 @@ describe('config', () => { expect(workspaces).to.deep.equal(['.']) }) }) + + describe('getOverrides', () => { + it('returns an empty object where there are no overrides', () => { + const {getOverrides} = factoryConfigMethods({}) + const overrides = getOverrides() + expect(overrides).to.be.deep.equal({}) + }) + + it('returns overrides', () => { + const overrides = { + literals: [ + { + regex: 'Lokalise:' + } + ] + } + const {getOverrides} = factoryConfigMethods({ + config: { + 'sui-mono': { + overrides + } + } + }) + + expect(getOverrides()).to.be.deep.equal(overrides) + }) + }) }) diff --git a/packages/sui-pde/CHANGELOG.md b/packages/sui-pde/CHANGELOG.md index 2594c1e1c..2d2b71dd2 100644 --- a/packages/sui-pde/CHANGELOG.md +++ b/packages/sui-pde/CHANGELOG.md @@ -1,5 +1,23 @@ # CHANGELOG +# 2.24.0 (2023-10-02) + + +### Features + +* **packages/sui-pde:** remove event by default make it optional. ([a716bc9](https://github.com/SUI-Components/sui/commit/a716bc984ffda306f5342693cc618e8ef43da3cb)) + + + +# 2.23.0 (2023-08-17) + + +### Features + +* **packages/sui-pde:** update version optimizely ([e0ab95e](https://github.com/SUI-Components/sui/commit/e0ab95e5697698116c018c7d34b34c1dcc2176b8)) + + + # 2.22.0 (2023-04-03) diff --git a/packages/sui-pde/README.md b/packages/sui-pde/README.md index 133a2929d..04ed7354b 100644 --- a/packages/sui-pde/README.md +++ b/packages/sui-pde/README.md @@ -229,6 +229,18 @@ const optimizelyAdapter = new OptimizelyAdapter({ }) ``` +#### Track Experiment Viewed + +In order to reduce unnecessary calls to Segment, the `Experiment Viewed` event is disabled by default. + +If you need to track how many times your experiment has been viewed, you should set the `shouldTrackExperimentViewed` argument to true. + +```js +const {isActive, variables} = useFeature('myFeatureKey', undefined, undefined, undefined, true) +``` + +A refactoring task is pending to transition the hook's positional parameters to named parameters. + #### Attributes In order to pass by attributes, you'll able to do so by adding the second argument as `attributes` when using the useFeature hook. Something like this: @@ -307,4 +319,4 @@ const MyComponent = () => { Regarding to [Segment documentation](https://segment.com/docs/connections/destinations/catalog/optimizely-web/#optimizely-full-stack-javascript-sdk) -Segment expects a single `window.optimizelyClientInstance` to exist in the browser, so when using multiple optimizely instances, events from multiple instances will be sent to a single Segment source, so the Segment destinations should be properly configured having this in consideration. \ No newline at end of file +Segment expects a single `window.optimizelyClientInstance` to exist in the browser, so when using multiple optimizely instances, events from multiple instances will be sent to a single Segment source, so the Segment destinations should be properly configured having this in consideration. diff --git a/packages/sui-pde/package.json b/packages/sui-pde/package.json index 94195181c..f00451d5e 100644 --- a/packages/sui-pde/package.json +++ b/packages/sui-pde/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/pde", - "version": "2.22.0", + "version": "2.24.0", "description": "", "type": "module", "main": "lib/index.js", @@ -17,7 +17,7 @@ "author": "", "license": "MIT", "dependencies": { - "@optimizely/optimizely-sdk": "4.5.1", + "@optimizely/optimizely-sdk": "4.9.4", "@s-ui/js": "2" }, "peerDependencies": { diff --git a/packages/sui-pde/src/hooks/useFeature.js b/packages/sui-pde/src/hooks/useFeature.js index fa5aa35f7..635c8bc3d 100644 --- a/packages/sui-pde/src/hooks/useFeature.js +++ b/packages/sui-pde/src/hooks/useFeature.js @@ -61,7 +61,8 @@ export default function useFeature( featureKey, attributes, queryString, - adapterId + adapterId, + shouldTrackExperimentViewed ) { const {pde} = useContext(PdeContext) if (pde === null) @@ -91,18 +92,20 @@ export default function useFeature( adapterId }) - trackFeatureFlagViewed({ - isActive, - trackExperimentViewed: strategy.trackExperiment, - featureKey - }) - trackLinkedExperimentsViewed({ - linkedExperiments, - trackExperimentViewed: strategy.trackExperiment, - pde, - attributes, - adapterId - }) + if (shouldTrackExperimentViewed) { + trackFeatureFlagViewed({ + isActive, + trackExperimentViewed: strategy.trackExperiment, + featureKey + }) + trackLinkedExperimentsViewed({ + linkedExperiments, + trackExperimentViewed: strategy.trackExperiment, + pde, + attributes, + adapterId + }) + } return {isActive, variables} } catch (error) { // eslint-disable-next-line no-console diff --git a/packages/sui-pde/test/common/useFeatureSpec.js b/packages/sui-pde/test/common/useFeatureSpec.js index af956ba5b..6e607350e 100644 --- a/packages/sui-pde/test/common/useFeatureSpec.js +++ b/packages/sui-pde/test/common/useFeatureSpec.js @@ -128,9 +128,19 @@ describe('when pde context is set', () => { }) it('should send the on state experiment viewed', () => { - renderHook(() => useFeature('activeFeatureFlagKey'), { - wrapper - }) + renderHook( + () => + useFeature( + 'activeFeatureFlagKey', + undefined, + undefined, + undefined, + true + ), + { + wrapper + } + ) expect(window.analytics.track.args[0][0]).to.equal( 'Experiment Viewed' ) @@ -142,12 +152,32 @@ describe('when pde context is set', () => { describe.client('when a feature is seen twice', () => { it('should only track one experiment viewed event', () => { - renderHook(() => useFeature('repeatedFeatureFlagKey'), { - wrapper - }) - renderHook(() => useFeature('repeatedFeatureFlagKey'), { - wrapper - }) + renderHook( + () => + useFeature( + 'repeatedFeatureFlagKey', + undefined, + undefined, + undefined, + true + ), + { + wrapper + } + ) + renderHook( + () => + useFeature( + 'repeatedFeatureFlagKey', + undefined, + undefined, + undefined, + true + ), + { + wrapper + } + ) expect(window.analytics.track.args.length).to.equal(1) }) }) @@ -184,9 +214,19 @@ describe('when pde context is set', () => { }) it('should send the off state experiment viewed', () => { - renderHook(() => useFeature('notActiveFeatureFlagKey'), { - wrapper - }) + renderHook( + () => + useFeature( + 'notActiveFeatureFlagKey', + undefined, + undefined, + undefined, + true + ), + { + wrapper + } + ) expect(window.analytics.track.args[0][0]).to.equal( 'Experiment Viewed' ) @@ -238,9 +278,19 @@ describe('when pde context is set', () => { }) it('should send experiment viewed event for every test asociated and the experiment viewed associated to the feature flag itself', () => { - renderHook(() => useFeature('featureKey4', {attribute1: 'value'}), { - wrapper - }) + renderHook( + () => + useFeature( + 'featureKey4', + {attribute1: 'value'}, + undefined, + undefined, + true + ), + { + wrapper + } + ) // feature being called expect(window.analytics.track.args[0][0]).to.equal('Experiment Viewed') @@ -315,12 +365,32 @@ describe('when pde context is set', () => { stubFactory(isFeatureEnabled) }) it('should send only one experiment viewed event', () => { - renderHook(() => useFeature('repeatedFeatureFlagKey'), { - wrapper - }) - renderHook(() => useFeature('repeatedFeatureFlagKey'), { - wrapper - }) + renderHook( + () => + useFeature( + 'repeatedFeatureFlagKey', + undefined, + undefined, + undefined, + true + ), + { + wrapper + } + ) + renderHook( + () => + useFeature( + 'repeatedFeatureFlagKey', + undefined, + undefined, + undefined, + true + ), + { + wrapper + } + ) expect(window.analytics.track.args.length).to.equal(1) }) }) @@ -338,12 +408,32 @@ describe('when pde context is set', () => { stubFactory(isFeatureEnabled) }) it('should send two experiment viewed events', () => { - renderHook(() => useFeature('repeatedFeatureFlagKey'), { - wrapper - }) - renderHook(() => useFeature('repeatedFeatureFlagKey'), { - wrapper - }) + renderHook( + () => + useFeature( + 'repeatedFeatureFlagKey', + undefined, + undefined, + undefined, + true + ), + { + wrapper + } + ) + renderHook( + () => + useFeature( + 'repeatedFeatureFlagKey', + undefined, + undefined, + undefined, + true + ), + { + wrapper + } + ) expect(window.analytics.track.args.length).to.equal(2) }) }) diff --git a/packages/sui-react-web-vitals/CHANGELOG.md b/packages/sui-react-web-vitals/CHANGELOG.md index e0f3a6a31..17b1c3fd0 100644 --- a/packages/sui-react-web-vitals/CHANGELOG.md +++ b/packages/sui-react-web-vitals/CHANGELOG.md @@ -1,5 +1,70 @@ # CHANGELOG +# 1.10.0 (2023-10-09) + + +### Features + +* **packages/sui-react-web-vitals:** update Wev Vitals lib to 3.5.0 ([15a4775](https://github.com/SUI-Components/sui/commit/15a47759b5580af5af748940859415f7e1aba334)) + + + +# 1.9.0 (2023-09-08) + + +### Bug Fixes + +* **packages/sui-react-web-vitals:** fix path is regexp logic ([f2427bc](https://github.com/SUI-Components/sui/commit/f2427bc686f81464e869adc4dabc2b006ed5b006)) + + +### Features + +* **packages/sui-react-web-vitals:** improve logic for pathname normalisation ([e214243](https://github.com/SUI-Components/sui/commit/e214243bdcd3f23dacdb1e91e2abd1fdc55aa8ee)) + + + +# 1.8.0 (2023-09-05) + + +### Bug Fixes + +* **packages/sui-react-web-vitals:** use metricsAllChanges and add more default metrics ([056247f](https://github.com/SUI-Components/sui/commit/056247fda7af44e0ce19ddce7fe1dc281160ef27)) +* **packages/sui-react-web-vitals:** validate if the rating is EQUAL to 'good' ([2655250](https://github.com/SUI-Components/sui/commit/265525050f7bbcf906ede1acf2efb22fc4febf23)) + + + +# 1.7.0 (2023-09-05) + + +### Features + +* **packages/sui-react-web-vitals:** use cwv instead of log ([e5be9ec](https://github.com/SUI-Components/sui/commit/e5be9ecc49d33db9b5201efe88b510632d5fa413)) + + + +# 1.6.0 (2023-08-10) + + +### Bug Fixes + +* **packages/sui-react-web-vitals:** fix path normalizer to avoid DD not collecting some tag values ([2acaa96](https://github.com/SUI-Components/sui/commit/2acaa96b46df2fa3ed83036424c08324a280de77)) + + + +# 1.5.0 (2023-08-08) + + +### Features + +* **packages/sui-react-web-vitals:** add attribution param ([6da1b1b](https://github.com/SUI-Components/sui/commit/6da1b1b541f31247a2d45f19060477c53d25b294)) +* **packages/sui-react-web-vitals:** add proper thresholds for all metrics ([24fbcda](https://github.com/SUI-Components/sui/commit/24fbcda3c6817cf45dcfb64526f0db9ff1dadfe5)) +* **packages/sui-react-web-vitals:** add reporting all changes for defined metrics ([cdabf48](https://github.com/SUI-Components/sui/commit/cdabf48f42ddf12c017d5970900e928f1ae9579c)) +* **packages/sui-react-web-vitals:** adding attribution as tags on metric ([1d7c76f](https://github.com/SUI-Components/sui/commit/1d7c76ff7c9a2d775eef49f8802ffaf1b6f8e000)) +* **packages/sui-react-web-vitals:** better way of managing reportAllChanges ([7058675](https://github.com/SUI-Components/sui/commit/70586755f10c7c2955b4d7811c23a8a16b7468c1)) +* **packages/sui-react-web-vitals:** use logger.log instead of logger.metric ([3e4726e](https://github.com/SUI-Components/sui/commit/3e4726e6516cb30c644847e8cc941b58734642e6)) + + + # 1.4.0 (2023-05-18) diff --git a/packages/sui-react-web-vitals/package.json b/packages/sui-react-web-vitals/package.json index fa59b50cb..d28c72480 100644 --- a/packages/sui-react-web-vitals/package.json +++ b/packages/sui-react-web-vitals/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/react-web-vitals", - "version": "1.4.0", + "version": "1.10.0", "description": "", "type": "module", "main": "lib/index.js", @@ -17,7 +17,7 @@ "author": "", "license": "MIT", "dependencies": { - "web-vitals": "3.1.0", + "web-vitals": "3.5.0", "@s-ui/react-hooks": "1" }, "peerDependencies": { diff --git a/packages/sui-react-web-vitals/src/index.js b/packages/sui-react-web-vitals/src/index.js index 91edeed84..dc930c480 100644 --- a/packages/sui-react-web-vitals/src/index.js +++ b/packages/sui-react-web-vitals/src/index.js @@ -1,19 +1,42 @@ import {useContext, useEffect, useRef} from 'react' import PropTypes from 'prop-types' -import * as reporter from 'web-vitals' +import * as reporter from 'web-vitals/attribution' import SUIContext from '@s-ui/react-context' import useMount from '@s-ui/react-hooks/lib/useMount/index.js' import {useRouter} from '@s-ui/react-router' export const METRICS = { - TTFB: 'TTFB', - LCP: 'LCP', CLS: 'CLS', + FCP: 'FCP', FID: 'FID', INP: 'INP', - FCP: 'FCP' + LCP: 'LCP', + TTFB: 'TTFB' +} + +// https://github.com/GoogleChrome/web-vitals#metric +const RATING = { + GOOD: 'good', + NEEDS_IMPROVEMENT: 'needs-improvement', + POOR: 'poor' +} + +const DEFAULT_METRICS_REPORTING_ALL_CHANGES = [ + METRICS.CLS, + METRICS.FID, + METRICS.INP, + METRICS.LCP +] + +const DEFAULT_CWV_THRESHOLDS = { + [METRICS.CLS]: 100, + [METRICS.FCP]: 1800, + [METRICS.FID]: 100, + [METRICS.INP]: 200, + [METRICS.LCP]: 2500, + [METRICS.TTFB]: 800 } export const DEVICE_TYPES = { @@ -23,18 +46,30 @@ export const DEVICE_TYPES = { } const getNormalizedPathname = pathname => { - return pathname.replaceAll('*', '_') + return pathname.replace(/[^a-z0-9]/gi, '') +} + +const getPathname = route => { + return route?.path || route?.regexp?.toString() +} + +const getHasPathOnRoute = route => { + return Boolean(route?.path) } export default function WebVitalsReporter({ - metrics = Object.values(METRICS), - pathnames, + children, deviceType, + metrics = Object.values(METRICS), + metricsAllChanges = DEFAULT_METRICS_REPORTING_ALL_CHANGES, onReport, - children + pathnames, + thresholds = DEFAULT_CWV_THRESHOLDS }) { const {logger, browser} = useContext(SUIContext) const router = useRouter() + const {routes} = router + const route = routes[routes.length - 1] const onReportRef = useRef(onReport) useEffect(() => { @@ -42,15 +77,7 @@ export default function WebVitalsReporter({ }, [onReport]) useMount(() => { - const getPathname = () => { - const {routes} = router - const route = routes[routes.length - 1] - return route?.path || route?.regexp?.toString() - } - const getRouteid = () => { - const {routes} = router - const route = routes[routes.length - 1] return route?.id } @@ -58,32 +85,60 @@ export default function WebVitalsReporter({ return deviceType || browser?.deviceType } - const handleReport = ({name, value}) => { + const getTarget = ({name, attribution}) => { + switch (name) { + case METRICS.CLS: + return attribution.largestShiftTarget + case METRICS.LCP: + return attribution.element + default: + return attribution.eventTarget + } + } + + const handleAllChanges = ({attribution, name, rating, value}) => { + const amount = name === METRICS.CLS ? value * 1000 : value + const pathname = getPathname(route) + const hasPathOnRoute = getHasPathOnRoute(route) + const isExcluded = + !pathname || (Array.isArray(pathnames) && !pathnames.includes(pathname)) + + if (isExcluded || !logger?.cwv || rating === RATING.GOOD) return + + const target = getTarget({name, attribution}) + + logger.cwv({ + name: `cwv.${name.toLowerCase()}`, + amount, + path: hasPathOnRoute ? pathname : getNormalizedPathname(pathname), + target, + loadState: attribution.loadState + }) + } + + const handleChange = ({name, value}) => { const onReport = onReportRef.current - const pathname = getPathname() + const pathname = getPathname(route) + const hasPathOnRoute = getHasPathOnRoute(route) const routeid = getRouteid() const type = getDeviceType() const isExcluded = !pathname || (Array.isArray(pathnames) && !pathnames.includes(pathname)) - if (isExcluded) { - return - } + if (isExcluded) return if (onReport) { onReport({ name, amount: value, - pathname, + pathname: hasPathOnRoute ? pathname : getNormalizedPathname(pathname), routeid, type }) return } - if (!logger?.distribution) { - return - } + if (!logger?.distribution) return const amount = name === METRICS.CLS ? value * 1000 : value @@ -97,7 +152,7 @@ export default function WebVitalsReporter({ }, { key: 'pathname', - value: getNormalizedPathname(pathname) + value: hasPathOnRoute ? pathname : getNormalizedPathname(pathname) }, ...(routeid ? [ @@ -120,7 +175,9 @@ export default function WebVitalsReporter({ } metrics.forEach(metric => { - reporter[`on${metric}`](handleReport) + reporter[`on${metric}`](handleChange) + if (metricsAllChanges.includes(metric)) + reporter[`on${metric}`](handleAllChanges, {reportAllChanges: true}) }) }) @@ -129,23 +186,33 @@ export default function WebVitalsReporter({ WebVitalsReporter.propTypes = { /** - * An optional array of core web vitals. Choose between: TTFB, LCP, FID, CLS and INP. Defaults to all. + * An optional children node */ - metrics: PropTypes.arrayOf(PropTypes.oneOf(Object.values(METRICS))), + children: PropTypes.node, /** * An optional string to identify the device type. Choose between: desktop, tablet and mobile */ deviceType: PropTypes.oneOf(Object.values(DEVICE_TYPES)), /** - * An optional array of pathnames that you want to track + * An optional array of core web vitals. Choose between: TTFB, LCP, FID, CLS and INP. Defaults to all. */ - pathnames: PropTypes.arrayOf(PropTypes.string), + metrics: PropTypes.arrayOf(PropTypes.oneOf(Object.values(METRICS))), + /** + * An optional array of core web vitals that will report on all changes. Choose between: TTFB, LCP, FID, CLS and INP. Defaults to LCP and INP. + */ + metricsAllChanges: PropTypes.arrayOf(PropTypes.oneOf(Object.values(METRICS))), /** * An optional callback to be used to track core web vitals */ onReport: PropTypes.func, /** - * An optional children node + * An optional array of pathnames that you want to track + */ + pathnames: PropTypes.arrayOf(PropTypes.string), + /** + * An object with METRICS as keys and thresholds as values + * Thresholds by default are those above which Google considers the page as "needs improvement" + * Lower thresholds could be set for fine-tuning, higher thresholds could be set for less noise when reporting all changes */ - children: PropTypes.node + thresholds: PropTypes.object } diff --git a/packages/sui-sass-loader/CHANGELOG.md b/packages/sui-sass-loader/CHANGELOG.md index 41e84c481..88fcfef89 100644 --- a/packages/sui-sass-loader/CHANGELOG.md +++ b/packages/sui-sass-loader/CHANGELOG.md @@ -1,5 +1,14 @@ # CHANGELOG +# 1.6.0 (2023-08-14) + + +### Bug Fixes + +* **packages/sui-sass-loader:** upgrade versions for security ([0a4f9c4](https://github.com/SUI-Components/sui/commit/0a4f9c4defb7c9d6c2ec52b174f051320702f640)) + + + # 1.5.0 (2022-12-23) diff --git a/packages/sui-sass-loader/package.json b/packages/sui-sass-loader/package.json index e1591609f..a29f96aa8 100644 --- a/packages/sui-sass-loader/package.json +++ b/packages/sui-sass-loader/package.json @@ -1,7 +1,7 @@ { "name": "@s-ui/sass-loader", "description": "Sass Loader for Webpack", - "version": "1.5.0", + "version": "1.6.0", "main": "src/index.js", "scripts": { "test": "npm run test:server", @@ -11,7 +11,7 @@ "cli-source-preview": "1.1.0", "co": "4.6.0", "fs-extra": "10.1.0", - "loader-utils": "3.2.0" + "loader-utils": "3.2.1" }, "peerDependencies": { "webpack": "5", @@ -22,6 +22,6 @@ "mini-css-extract-plugin": "2.6.1", "sass": "1.54.5", "sass-loader": "12", - "webpack": "5.74.0" + "webpack": "5.82.1" } } diff --git a/packages/sui-ssr/CHANGELOG.md b/packages/sui-ssr/CHANGELOG.md index d214a9f27..c1c7cabe9 100644 --- a/packages/sui-ssr/CHANGELOG.md +++ b/packages/sui-ssr/CHANGELOG.md @@ -1,5 +1,50 @@ # CHANGELOG +# 8.16.0 (2023-08-22) + + +### Bug Fixes + +* **packages/sui-ssr:** update express dep to fix security issue ([10f4b05](https://github.com/SUI-Components/sui/commit/10f4b0514b573c9e201ab5942c3fb259cc45ef01)) + + + +# 8.15.0 (2023-08-22) + + +### Bug Fixes + +* **packages/sui-ssr:** remove copy-paste dependency to fix security issue ([2ac2885](https://github.com/SUI-Components/sui/commit/2ac28855ff5890dd72d034368c572503c9022046)) + + + +# 8.14.0 (2023-08-21) + + +### Bug Fixes + +* **packages/sui-ssr:** update dep for security issue ([34706c9](https://github.com/SUI-Components/sui/commit/34706c95ca06e55422ad333961781f00305a456e)) + + + +# 8.13.0 (2023-08-17) + + +### Features + +* **packages/sui-ssr:** update gir-url-parse version ([437c183](https://github.com/SUI-Components/sui/commit/437c1837a8fa089be126d5c7bf2b6e977805cb02)) + + + +# 8.12.0 (2023-08-14) + + +### Bug Fixes + +* **packages/sui-ssr:** upgrade versions for security ([9566aac](https://github.com/SUI-Components/sui/commit/9566aacb02bf080ddb17fe519ec5ec94d8e418bd)) + + + # 8.11.0 (2022-10-06) diff --git a/packages/sui-ssr/bin/sui-ssr-build.js b/packages/sui-ssr/bin/sui-ssr-build.js index 40611baba..7057d3359 100755 --- a/packages/sui-ssr/bin/sui-ssr-build.js +++ b/packages/sui-ssr/bin/sui-ssr-build.js @@ -4,7 +4,6 @@ const program = require('commander') const path = require('path') const {readFileSync, rmSync, writeFileSync} = require('fs') const {copyFile} = require('fs/promises') -const ncp = require('copy-paste') const webpack = require('webpack') const linkLoaderConfigBuilder = require('@s-ui/bundler/loaders/linkLoaderConfigBuilder') @@ -74,9 +73,6 @@ const build = () => jsonStats.warnings.map(warning => console.log(warning)) } - const [serverEntryPoint] = jsonStats.assetsByChunkName.main - const SERVER_ENTRY_POINT = path.join(BUILD_SERVER_PATH, serverEntryPoint) - const html = readFileSync(path.join(PUBLIC_PATH, 'index.html'), 'utf-8') const htmlWithoutThirdParties = removeMarkedTags(html) writeFileSync( @@ -101,8 +97,6 @@ const build = () => copy500.status === 'fulfilled' && console.log('500.html copied!') verbose && console.log(`Webpack stats: ${stats}`) - console.log(`Server entry point copy to clipboard ${SERVER_ENTRY_POINT}`) - ncp.copy(SERVER_ENTRY_POINT) resolve() }) diff --git a/packages/sui-ssr/package.json b/packages/sui-ssr/package.json index c4af2fd3b..f12e5bde4 100644 --- a/packages/sui-ssr/package.json +++ b/packages/sui-ssr/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/ssr", - "version": "8.11.0", + "version": "8.16.0", "description": "> Plug SSR to you SUI SPA.", "main": "index.js", "bin": { @@ -30,14 +30,13 @@ "archiver": "5.3.0", "commander": "8.3.0", "compression": "1.7.4", - "copy-paste": "1.3.0", - "express": "4.17.2", + "express": "4.18.2", "express-basic-auth": "1.2.1", - "git-url-parse": "12.0.0", + "git-url-parse": "13.1.0", "jsesc": "3.0.2", "mime": "1.6.0", "noop-console": "0.8.0", "parse5": "6.0.1", - "ua-parser-js": "0.7.31" + "ua-parser-js": "0.7.33" } } diff --git a/packages/sui-studio/CHANGELOG.md b/packages/sui-studio/CHANGELOG.md index a8c1df450..e9616ebaf 100644 --- a/packages/sui-studio/CHANGELOG.md +++ b/packages/sui-studio/CHANGELOG.md @@ -1,5 +1,32 @@ # CHANGELOG +# 11.36.0 (2023-08-17) + + +### Features + +* **packages/sui-studio:** update gir-url-parse version ([d4c9ef4](https://github.com/SUI-Components/sui/commit/d4c9ef466b1cff99b0f4714949dcb790f5f5b27e)) + + + +# 11.35.0 (2023-07-18) + + +### Bug Fixes + +* **packages/sui-studio:** update studio postinstall ([7f78df7](https://github.com/SUI-Components/sui/commit/7f78df7023299e60be30cb1c5256748e5ac1d609)) + + + +# 11.34.0 (2023-07-18) + + +### Features + +* **packages/sui-studio:** update studio generate install to avoid installing peer deps ([815dba9](https://github.com/SUI-Components/sui/commit/815dba96b2dae34f159f43ab85fd926a3a2a74e3)) + + + # 11.33.0 (2022-12-23) diff --git a/packages/sui-studio/bin/sui-studio-generate.js b/packages/sui-studio/bin/sui-studio-generate.js index 2ac276faa..5f2acd51b 100755 --- a/packages/sui-studio/bin/sui-studio-generate.js +++ b/packages/sui-studio/bin/sui-studio-generate.js @@ -309,7 +309,13 @@ export default () => <${componentInPascal} /> writeFile(COMPONENT_TEST_FILE, removeRepeatedNewLines(testTemplate)) ]).then(() => { console.log(colors.gray(`[${packageName}]: Installing the dependencies`)) - const install = spawn('npm', ['install'], {cwd: COMPONENT_PATH}) + const install = spawn('npm', [ + 'install', + '--legacy-peer-deps', + '--no-audit', + '--no-fund', + '--production=false' + ]) install.stdout.on('data', data => console.log(colors.gray(`[${packageName}]: ${data.toString()}`)) diff --git a/packages/sui-studio/package.json b/packages/sui-studio/package.json index 0420ce10c..99d6f1701 100644 --- a/packages/sui-studio/package.json +++ b/packages/sui-studio/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/studio", - "version": "11.33.0", + "version": "11.36.0", "description": "Develop, maintain and publish your SUI components.", "main": "index.js", "bin": { @@ -26,7 +26,7 @@ "deepmerge": "4.2.2", "fast-glob": "3.2.11", "fs-extra": "10.1.0", - "git-url-parse": "12.0.0", + "git-url-parse": "13.1.0", "hoist-non-react-statics": "3.3.2", "just-debounce-it": "1.5.0", "just-kebab-case": "1.1.0", diff --git a/packages/sui-svg/CHANGELOG.md b/packages/sui-svg/CHANGELOG.md index 337a81e8a..8c7e22ae0 100644 --- a/packages/sui-svg/CHANGELOG.md +++ b/packages/sui-svg/CHANGELOG.md @@ -1,5 +1,23 @@ # CHANGELOG +# 3.23.0 (2023-08-14) + + +### Bug Fixes + +* **packages/sui-svg:** upgrade versions for security ([18bc1fe](https://github.com/SUI-Components/sui/commit/18bc1fe0609617865591759a4887f50aa09efac3)) + + + +# 3.22.0 (2023-07-24) + + +### Features + +* **packages/sui-svg:** add sui svg dist ([374edc4](https://github.com/SUI-Components/sui/commit/374edc4f66e72f0ec04ca25bbeabb26ad8c5cbfa)) + + + # 3.21.0 (2022-08-22) diff --git a/packages/sui-svg/bin/sui-svg-dist.js b/packages/sui-svg/bin/sui-svg-dist.js new file mode 100644 index 000000000..d2bf4e928 --- /dev/null +++ b/packages/sui-svg/bin/sui-svg-dist.js @@ -0,0 +1,42 @@ +#!/usr/bin/env node + +import {resolve} from 'path' + +import {build} from 'vite' + +const {pathname: root} = new URL('../src', import.meta.url) + +const outDir = resolve(process.cwd(), './public') +const icons = resolve(process.cwd(), 'lib', '_demo.js') + +console.log('[sui-svg] Preparing build with icons...') + +await build({ + root, + optimizeDeps: { + include: [ + 'classnames', + 'prop-types', + 'react', + 'react/jsx-runtime', + 'react-dom' + ] + }, + resolve: { + alias: [ + { + find: '@s-ui/svg-icons', + replacement: () => icons + }, + { + find: /^~.+/, + replacement: val => val.replace(/^~/, '') + } + ] + }, + build: { + outDir + } +}) + +console.log('[sui-svg] Build is ready! ✅') diff --git a/packages/sui-svg/bin/sui-svg.js b/packages/sui-svg/bin/sui-svg.js index 994eb5604..b2848acd0 100755 --- a/packages/sui-svg/bin/sui-svg.js +++ b/packages/sui-svg/bin/sui-svg.js @@ -11,5 +11,6 @@ program.version(version, ' --version') program.command('build', 'Builds React components from svg files') program.command('demo', 'Loads a local static website') +program.command('dist', 'Generates static website') program.parse(process.argv) diff --git a/packages/sui-svg/package.json b/packages/sui-svg/package.json index 069d9c645..5d016a756 100644 --- a/packages/sui-svg/package.json +++ b/packages/sui-svg/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/svg", - "version": "3.21.0", + "version": "3.23.0", "description": "Converts svg files into React Components", "main": "index.js", "type": "module", @@ -22,7 +22,7 @@ "react-dom": "17", "sass": "1", "svgo": "2.8.0", - "vite": "3.0.9" + "vite": "3.2.7" }, "license": "MIT" } diff --git a/packages/sui-test-contract/package.json b/packages/sui-test-contract/package.json index a0fc92b52..241bdba3c 100644 --- a/packages/sui-test-contract/package.json +++ b/packages/sui-test-contract/package.json @@ -20,7 +20,7 @@ "@pactflow/pact-msw-adapter": "1.5.0", "@s-ui/mock": "beta", "commander": "8.3.0", - "headers-polyfill": "3.0.10" + "headers-polyfill": "3.1.2" }, "devDependencies": { "@s-ui/domain": "2" diff --git a/packages/sui-test/CHANGELOG.md b/packages/sui-test/CHANGELOG.md index 1b5ee6617..86f68c5f0 100644 --- a/packages/sui-test/CHANGELOG.md +++ b/packages/sui-test/CHANGELOG.md @@ -1,5 +1,18 @@ # CHANGELOG +# 8.27.0 (2023-08-14) + + +### Bug Fixes + +* **packages/sui-test:** upgrade versions for security ([9b1c7d1](https://github.com/SUI-Components/sui/commit/9b1c7d1c3148f3adc7ee35e5970e8ae866e1e5e8)) + + + +# 8.26.0 (2023-06-12) + + + # 8.25.0 (2023-05-24) diff --git a/packages/sui-test/package.json b/packages/sui-test/package.json index fb6fea183..de20a5eae 100644 --- a/packages/sui-test/package.json +++ b/packages/sui-test/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/test", - "version": "8.25.0", + "version": "8.27.0", "description": "", "bin": { "sui-test": "bin/sui-test.js" @@ -40,6 +40,6 @@ "stream-browserify": "3.0.0", "tty-browserify": "0.0.1", "util": "0.12.4", - "webpack": "5.74.0" + "webpack": "5.82.1" } } diff --git a/packages/sui-widget-embedder/CHANGELOG.md b/packages/sui-widget-embedder/CHANGELOG.md index e4871d30b..95dd3fdcd 100644 --- a/packages/sui-widget-embedder/CHANGELOG.md +++ b/packages/sui-widget-embedder/CHANGELOG.md @@ -1,5 +1,14 @@ # CHANGELOG +# 6.2.0 (2023-08-21) + + +### Bug Fixes + +* **packages/sui-widget-embedder:** update dep for security issue ([882de3a](https://github.com/SUI-Components/sui/commit/882de3af722ac2940c203a0e9a3db95c653c8d2d)) + + + # 6.1.0 (2022-06-07) diff --git a/packages/sui-widget-embedder/package.json b/packages/sui-widget-embedder/package.json index f8d5e6a60..0b6af3717 100644 --- a/packages/sui-widget-embedder/package.json +++ b/packages/sui-widget-embedder/package.json @@ -1,6 +1,6 @@ { "name": "@s-ui/widget-embedder", - "version": "6.1.0", + "version": "6.2.0", "description": "Embed React components to your app as widgets", "bin": { "sui-widget-embedder": "./bin/sui-widget-embedder.js"