diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..5b311f84b --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,111 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + checks: + name: Checks + runs-on: ubuntu-latest + steps: + - name: Cancel Previous Redundant Builds + uses: styfle/cancel-workflow-action@0.6.0 + with: + access_token: ${{ github.token }} + + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Setup Node + uses: actions/setup-node@v4 + with: + registry-url: 'https://registry.npmjs.org' + node-version-file: '.nvmrc' + + - name: Setup Chrome + uses: browser-actions/setup-chrome@latest + + - name: Install + run: | + sudo apt-get install xvfb + npm install --no-save --no-fund --no-audit --legacy-peer-deps + npx -y ultra-runner --raw --recursive prepublishOnly + + - name: Lint + run: npm run lint + + - name: Type Checking + run: npm run types:check + + - name: Tests + run: | + npm run test:server:ci + xvfb-run --auto-servernum npm run test:client:ci + + publish: + name: Publish + needs: checks + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Setup Node + uses: actions/setup-node@v4 + with: + registry-url: 'https://registry.npmjs.org' + node-version-file: '.nvmrc' + + - name: Publish Packages + if: github.ref == 'refs/heads/master' + run: | + npx -p ./packages/sui-mono sui-mono check + 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 }} + + publish-tagged-packages: + name: Publish Tagged Packages to NPM + needs: checks + if: github.ref != 'refs/heads/master' && contains(github.event.pull_request.labels.*.name, 'feature') + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Setup Node + uses: actions/setup-node@v4 + with: + registry-url: 'https://registry.npmjs.org' + node-version-file: '.nvmrc' + + - name: Install + run: npm install -D commander execa prettier + + - name: Get Files + id: files + uses: jitterbit/get-changed-files@v1 + with: + format: 'json' + + - name: Publish Packages + run: npm run publish-tagged-packages -- --tag '${{ github.event.pull_request.head.ref }}' --files '${{ steps.files.outputs.added_modified }}' + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml deleted file mode 100644 index 811920831..000000000 --- a/.github/workflows/node.js.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: CI - -on: - push: - branches: [master] - pull_request: - branches: [master] - -jobs: - checks-and-release: - name: Checks & Release - runs-on: ubuntu-latest - steps: - - name: Cancel Previous Redundant Builds - uses: styfle/cancel-workflow-action@0.6.0 - with: - access_token: ${{ github.token }} - - - name: Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Setup Node - uses: actions/setup-node@v2 - with: - node-version-file: '.nvmrc' - registry-url: 'https://registry.npmjs.org' - - - name: Setup Chrome - uses: browser-actions/setup-chrome@latest - - - name: Install - run: | - sudo apt-get install xvfb - npm install --no-save --no-fund --no-audit --legacy-peer-deps - npx -y ultra-runner --raw --recursive prepublishOnly &>/dev/null - - - name: Lint - run: npm run lint - - - name: Tests - run: | - npm run test:server:ci - xvfb-run --auto-servernum npm run test:client:ci - - - name: Release - if: github.ref == 'refs/heads/master' - run: | - npx -p ./packages/sui-mono sui-mono check - 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 18fe87d62..0e3e749e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,25 @@ -node_modules/ -lib/ -coverage/ .DS_Store -npm-debug.log -.tern-port -package-lock.json +.idea/ .python-version +.tern-port +.vscode/ + +contract/logs/pact.log +coverage/ jsconfig.json +lib/ +node_modules/ +npm-debug.log +package-lock.json + +!packages/sui-sass-loader/test/server/fixtures/**/node_modules packages/sui-codemod/COMMANDS -sui-components-dashboard-9350df448fcd.json -.idea/ -.vscode/ packages/sui-studio/test/server/integration/empty-studio/components/fake/** +packages/sui-studio/test/server/integration/empty-studio/components/fake/componentnvim packages/sui-studio/test/server/integration/empty-studio/demo/fake/** packages/sui-studio/test/server/integration/empty-studio/test/fake/** -packages/sui-studio/test/server/integration/sample-studio/node_modules/ packages/sui-studio/test/server/integration/sample-studio/components/atom/button/node_modules/ +packages/sui-studio/test/server/integration/sample-studio/node_modules/ packages/sui-studio/test/server/integration/sample-studio/public -packages/sui-studio/test/server/integration/empty-studio/components/fake/componentnvim -!packages/sui-sass-loader/test/server/fixtures/**/node_modules - -contract/logs/pact.log -.npmrc \ No newline at end of file +sui-components-dashboard-9350df448fcd.json \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..e9ee3cb4d --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +legacy-peer-deps=true \ No newline at end of file diff --git a/package.json b/package.json index e41587415..f7a93aa3b 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "packages/*" ], "scripts": { - "phoenix": "npx @s-ui/mono phoenix && npx -y ultra-runner --raw --recursive prepublishOnly &>/dev/null", + "phoenix": "npx @s-ui/mono phoenix && npx -y ultra-runner --raw --recursive prepublishOnly --build &>/dev/null", "co": "npx @s-ui/mono commit", "lint": "sui-lint js && sui-lint sass", "test": "npm run test:client && npm run test:server", @@ -20,12 +20,14 @@ "test:server:ci": "npm run test:server", "test:client": "npx @s-ui/test browser -P 'packages/**/!(server)/*Spec.js'", "test:client:watch": "npm run test:client -- --watch", - "test:server": "npx @s-ui/test server -P './packages/sui-test-contract/test/server/setupSpec.js'", + "test:server": "npx @s-ui/test server -P 'packages/{sui-test-contract,sui-js-compiler}/**/server/*Spec.js'", "test:server:watch": "npm run test:server -- --watch", "test:e2e": "node ./packages/sui-studio/test/server/integration/static-server.js ./packages/sui-studio/test/server/integration/sample-studio/public && npx @s-ui/test-e2e --baseUrl=http://localhost:1234", "pre-commit": "sui-lint js --staged && sui-lint sass --staged", - "pre-push": "npm run test", - "commit-msg": "validate-commit-msg" + "pre-push": "npm run test && npm run types:check", + "publish-tagged-packages": "node ./scripts/publish-tagged-packages.mjs", + "commit-msg": "validate-commit-msg", + "types:check": "tsc" }, "devDependencies": { "@babel/cli": "7", @@ -33,9 +35,10 @@ "@s-ui/precommit": "3", "@s-ui/react-context": "1", "chai": "4.3.6", + "pino-pretty": "10.3.1", "react": "17", "sinon": "10.0.0", - "typescript": "4.3.2", + "typescript": "5.0.4", "validate-commit-msg": "2.14.0" }, "config": { @@ -79,4 +82,4 @@ "stylelint": { "extends": "./node_modules/@s-ui/lint/stylelint.config.js" } -} +} \ No newline at end of file diff --git a/packages/sui-bundler/bin/sui-bundler-dev.js b/packages/sui-bundler/bin/sui-bundler-dev.js index 703878cbb..37ce14eba 100755 --- a/packages/sui-bundler/bin/sui-bundler-dev.js +++ b/packages/sui-bundler/bin/sui-bundler-dev.js @@ -57,7 +57,7 @@ if (!module.parent) { const start = async ({config = webpackConfig, packagesToLink = program.opts().linkPackage || []} = {}) => { clearConsole() // Warn and crash if required files are missing - if (!checkRequiredFiles([path.join(config.context, 'index.html'), path.join(config.context, 'app.js')])) { + if (!checkRequiredFiles([path.join(config.context, 'index.html')])) { log.error(`โœ– Required files are missing, create and index.html and app.js inside your src folder.`) process.exit(1) } diff --git a/packages/sui-bundler/loaders/linkLoaderConfigBuilder.js b/packages/sui-bundler/loaders/linkLoaderConfigBuilder.js index b127e282c..147f11541 100644 --- a/packages/sui-bundler/loaders/linkLoaderConfigBuilder.js +++ b/packages/sui-bundler/loaders/linkLoaderConfigBuilder.js @@ -43,7 +43,7 @@ module.exports = ({config, packagesToLink, linkAll}) => { * if neccesary */ const linkLoader = { - test: /\.(jsx?|scss)$/, + test: /\.(jsx?|tsx?|scss)$/, enforce: 'pre', // this will ensure is execute before transformations use: { loader: require.resolve('./LinkLoader'), diff --git a/packages/sui-bundler/package.json b/packages/sui-bundler/package.json index 1d21107d2..001cb0614 100644 --- a/packages/sui-bundler/package.json +++ b/packages/sui-bundler/package.json @@ -7,6 +7,7 @@ }, "main": "./bin/sui-bundler.js", "scripts": { + "postinstall": "node ./scripts/postinstall.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], @@ -21,11 +22,14 @@ }, "homepage": "https://github.com/SUI-Components/sui/tree/master/packages/sui-bundler#readme", "dependencies": { - "@babel/core": "7.18.10", + "@babel/core": "7.21.8", "@builder.io/react-hydration-overlay": "0.0.8", "@pmmmwh/react-refresh-webpack-plugin": "0.5.10", + "@s-ui/compiler-config": "1", "@s-ui/helpers": "1", "@s-ui/sass-loader": "1", + "@swc/core": "1.3.14", + "@swc/helpers": "0.4.12", "address": "1.2.2", "autoprefixer": "10.4.8", "babel-loader": "8.3.0", @@ -36,7 +40,7 @@ "css-minimizer-webpack-plugin": "4.0.0", "esbuild": "0.15.5", "escape-string-regexp": "4.0.0", - "fast-glob": "3.2.11", + "fast-glob": "3.2.12", "find-free-ports": "3.0.0", "html-webpack-inject-attributes-plugin": "1.0.6", "html-webpack-plugin": "5.5.0", @@ -50,6 +54,7 @@ "stream-http": "3.2.0", "strip-ansi": "6.0.1", "style-loader": "3.3.1", + "swc-loader": "0.2.1", "url": "0.11.0", "webpack-dev-server": "4.15.2", "webpack-manifest-plugin": "5.0.0", diff --git a/packages/sui-bundler/scripts/postinstall.js b/packages/sui-bundler/scripts/postinstall.js new file mode 100755 index 000000000..59ef0954d --- /dev/null +++ b/packages/sui-bundler/scripts/postinstall.js @@ -0,0 +1,48 @@ +#!/usr/bin/env node +/* eslint-disable no-console */ + +const crypto = require('crypto') +const fs = require('fs-extra') +const path = require('path') +const {writeFile} = require('@s-ui/helpers/file.js') + +const {INIT_CWD} = process.env +const tsConfigTemplate = `\ +{ + "extends": "@s-ui/bundler/tsconfig.json", + "compilerOptions": { + "rootDir": "./" + }, + "include": ["src", "domain", "components"] +}` + +const md5 = str => crypto.createHash('md5').update(str).digest('hex') +const TS_CONFIG_PATH = path.join(INIT_CWD, 'tsconfig.json') +const PACKAGE_JSON_CONFIG_PATH = path.join(INIT_CWD, 'package.json') + +const config = require(PACKAGE_JSON_CONFIG_PATH)?.config?.['sui-bundler'] || {} + +const shouldGenerateTSConfig = () => { + try { + if (!config?.type || config?.type !== 'typescript') return false + + if (!fs.existsSync(TS_CONFIG_PATH)) return true + + const tsConfigLocal = fs.readFileSync(TS_CONFIG_PATH, {encoding: 'utf8'}) + return md5(tsConfigLocal) !== md5(tsConfigTemplate) + } catch (err) { + return true + } +} + +async function main() { + console.log('๐Ÿ” [sui-bundler postinstall] Checking if tsconfig.json is up to date...') + if (!shouldGenerateTSConfig()) { + console.log('โœ… [sui-bundler postinstall] tsconfig.json is up to date') + process.exit(0) + } + await writeFile(TS_CONFIG_PATH, tsConfigTemplate) + console.log('โŒ [sui-bundler postinstall] tsconfig.json was not up to date, so we updated it') +} + +main() diff --git a/packages/sui-bundler/shared/define.js b/packages/sui-bundler/shared/define.js index 0bb5ae7cf..c23cfcd32 100644 --- a/packages/sui-bundler/shared/define.js +++ b/packages/sui-bundler/shared/define.js @@ -6,10 +6,20 @@ if (process.platform === 'win32') { process.env.PWD = process.cwd() } +const {MAGIC_STRINGS = '{}'} = process.env + +let magic +try { + magic = JSON.parse(MAGIC_STRINGS) +} catch (err) { + magic = {} +} + module.exports = (vars = {}) => new webpack.DefinePlugin({ - __MOCKS_API_PATH__: JSON.stringify(process.env.MOCKS_API_PATH || process.env.PWD + '/mocks/routes'), __DEV__: false, __BASE_DIR__: JSON.stringify(process.env.PWD), - ...vars + __MOCKS_API_PATH__: JSON.stringify(process.env.MOCKS_API_PATH || process.env.PWD + '/mocks/routes'), + ...vars, + ...Object.fromEntries(Object.entries(magic).map(([key, value]) => [key, JSON.stringify(value)])) }) diff --git a/packages/sui-bundler/shared/index.js b/packages/sui-bundler/shared/index.js index a112101ea..2c9c59415 100644 --- a/packages/sui-bundler/shared/index.js +++ b/packages/sui-bundler/shared/index.js @@ -1,6 +1,6 @@ const {config} = require('./config.js') -exports.MAIN_ENTRY_POINT = './app.js' +exports.MAIN_ENTRY_POINT = './app' exports.config = config exports.cleanList = list => list.filter(Boolean) diff --git a/packages/sui-bundler/shared/module-rules-babel.js b/packages/sui-bundler/shared/module-rules-babel.js deleted file mode 100644 index 52126e5fa..000000000 --- a/packages/sui-bundler/shared/module-rules-babel.js +++ /dev/null @@ -1,33 +0,0 @@ -const path = require('path') -const {config} = require('./index.js') - -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, isDevelopment = false, supportLegacyBrowsers = true} = {}) => ({ - test: /\.jsx?$/, - exclude: EXCLUDED_FOLDERS_REGEXP, - use: [ - { - loader: require.resolve('babel-loader'), - options: { - cacheDirectory: true, - cacheCompression: false, - babelrc: false, - compact: true, - plugins: [isDevelopment && require.resolve('react-refresh/babel')].filter(Boolean), - presets: [ - [ - require.resolve('babel-preset-sui'), - { - isServer, - isModern: !supportLegacyBrowsers, - targets: config.targets - } - ] - ] - } - } - ] -}) diff --git a/packages/sui-bundler/shared/module-rules-compiler.js b/packages/sui-bundler/shared/module-rules-compiler.js new file mode 100644 index 000000000..3145cc9ff --- /dev/null +++ b/packages/sui-bundler/shared/module-rules-compiler.js @@ -0,0 +1,69 @@ +/* eslint-disable no-console */ +const fs = require('fs-extra') +const path = require('path') +const {config} = require('./index.js') +const {getSWCConfig} = require('@s-ui/compiler-config') + +const EXCLUDED_FOLDERS_REGEXP = new RegExp( + `node_modules(?!${path.sep}@s-ui(${path.sep}studio)(${path.sep}workbench)?${path.sep}src)` +) + +const getTSConfig = () => { + // Get TS config from the package dir. + const tsConfigPath = path.join(process.cwd(), 'tsconfig.json') + let tsConfig + + try { + if (fs.existsSync(tsConfigPath)) { + tsConfig = JSON.parse(fs.readFileSync(tsConfigPath, {encoding: 'utf8'})) + } + } catch (err) { + console.error(err) + } + + return tsConfig +} + +module.exports = ({isServer = false, isDevelopment = false, supportLegacyBrowsers = true} = {}) => { + const tsConfig = getTSConfig() + // If TS config exists in root dir, set TypeScript as enabled. + const isTypeScriptEnabled = Boolean(tsConfig) + + return isTypeScriptEnabled + ? { + test: /\.(js|ts)x?$/, + exclude: EXCLUDED_FOLDERS_REGEXP, + use: [ + { + loader: require.resolve('swc-loader'), + options: getSWCConfig({isModern: false, isTypeScript: true}) + } + ] + } + : { + test: /\.jsx?$/, + exclude: EXCLUDED_FOLDERS_REGEXP, + use: [ + { + loader: require.resolve('babel-loader'), + options: { + cacheDirectory: true, + cacheCompression: false, + babelrc: false, + compact: true, + plugins: [isDevelopment && require.resolve('react-refresh/babel')].filter(Boolean), + presets: [ + [ + require.resolve('babel-preset-sui'), + { + isServer, + isModern: !supportLegacyBrowsers, + targets: config.targets + } + ] + ] + } + } + ] + } +} diff --git a/packages/sui-bundler/shared/resolve-alias.js b/packages/sui-bundler/shared/resolve-alias.js index de6e0dcf7..8cdfb6192 100644 --- a/packages/sui-bundler/shared/resolve-alias.js +++ b/packages/sui-bundler/shared/resolve-alias.js @@ -1,5 +1,6 @@ const path = require('path') const {config} = require('./config.js') +const fs = require('fs') const {PWD} = process.env @@ -9,16 +10,40 @@ const {PWD} = process.env * So you should use the exact same imported file from node_modules, and the linked package * would try to use another different from its own node_modules. This will prevent that. */ -const defaultPackagesToAlias = ['react', 'react-router-dom', '@s-ui/pde', '@s-ui/react-context', '@s-ui/react-router'] +const defaultPackagesToAlias = [ + 'react', + 'react-dom', + 'react-router-dom', + 'react/jsx-dev-runtime', + 'react/jsx-runtime', + '@s-ui/pde', + '@s-ui/react-context', + '@s-ui/react-router' +] -const createAliasPath = pkgName => path.resolve(path.join(PWD, `./node_modules/${pkgName}`)) +const createAliasPath = pkgName => { + const nodeModulesPath = path.join(PWD, './node_modules') + const parentNodeModulesPath = path.join(PWD, '../node_modules') + const pkgPath = nodeModulesPath && path.join(nodeModulesPath, pkgName) -const mustPackagesToAlias = { - 'react/jsx-dev-runtime': 'react/jsx-dev-runtime.js', - 'react/jsx-runtime': 'react/jsx-runtime.js' + if (pkgPath && fs.existsSync(pkgPath)) { + return path.resolve(pkgPath) + } else if (fs.existsSync(parentNodeModulesPath)) { + return path.resolve(path.join(parentNodeModulesPath, pkgName)) + } + + try { + return require.resolve(pkgName).replace(/\/index\.js$/, '') + } catch (e) { + return '' + } } -exports.defaultAlias = Object.fromEntries(defaultPackagesToAlias.map(pkgName => [pkgName, createAliasPath(pkgName)])) +const mustPackagesToAlias = {} + +exports.defaultAlias = Object.fromEntries( + defaultPackagesToAlias.map(pkgName => [pkgName, createAliasPath(pkgName)]).filter(([, path]) => path) +) const aliasFromConfig = config.alias ? Object.entries(config.alias).reduce( diff --git a/packages/sui-bundler/tsconfig.json b/packages/sui-bundler/tsconfig.json new file mode 100644 index 000000000..c4f3b5e4a --- /dev/null +++ b/packages/sui-bundler/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@s-ui/compiler-config/tsconfig.json" +} diff --git a/packages/sui-bundler/webpack.config.dev.js b/packages/sui-bundler/webpack.config.dev.js index 94b1547ac..54761457c 100644 --- a/packages/sui-bundler/webpack.config.dev.js +++ b/packages/sui-bundler/webpack.config.dev.js @@ -12,7 +12,7 @@ const {aliasFromConfig, defaultAlias} = require('./shared/resolve-alias.js') const {supportLegacyBrowsers, cacheDirectory} = require('./shared/config.js') const {resolveLoader} = require('./shared/resolve-loader.js') -const createBabelRules = require('./shared/module-rules-babel.js') +const createCompilerRules = require('./shared/module-rules-compiler.js') const outputPath = path.join(process.cwd(), 'dist') @@ -42,7 +42,7 @@ const webpackConfig = { timers: false }, modules: ['node_modules', path.resolve(process.cwd())], - extensions: ['.js', '.json'] + extensions: ['.js', '.tsx', '.ts', '.json'] }, stats: 'errors-only', entry: { @@ -87,7 +87,7 @@ const webpackConfig = { resolveLoader, module: { rules: cleanList([ - createBabelRules({supportLegacyBrowsers, isDevelopment: true}), + createCompilerRules({supportLegacyBrowsers, isDevelopment: true}), { test: /(\.css|\.scss)$/, use: cleanList([ diff --git a/packages/sui-bundler/webpack.config.lib.js b/packages/sui-bundler/webpack.config.lib.js index e8d8d846c..33c06af3a 100644 --- a/packages/sui-bundler/webpack.config.lib.js +++ b/packages/sui-bundler/webpack.config.lib.js @@ -4,7 +4,7 @@ const {cleanList, envVars, MAIN_ENTRY_POINT, config} = require('./shared/index.j const path = require('path') const minifyJs = require('./shared/minify-js.js') const definePlugin = require('./shared/define.js') -const createBabelRules = require('./shared/module-rules-babel.js') +const createCompilerRules = require('./shared/module-rules-compiler.js') const sassRules = require('./shared/module-rules-sass.js') const {extractComments, sourceMap, supportLegacyBrowsers} = require('./shared/config.js') const {aliasFromConfig} = require('./shared/resolve-alias.js') @@ -45,7 +45,7 @@ module.exports = { }, plugins: cleanList([ new webpack.ProvidePlugin({ - process: 'process/browser' + process: 'process/browser.js' }), new MiniCssExtractPlugin({ filename: cssFileName, @@ -58,6 +58,6 @@ module.exports = { definePlugin() ]), module: { - rules: [createBabelRules({supportLegacyBrowsers}), sassRules] + rules: [createCompilerRules({supportLegacyBrowsers}), sassRules] } } diff --git a/packages/sui-bundler/webpack.config.prod.js b/packages/sui-bundler/webpack.config.prod.js index 7e81eebca..945674268 100644 --- a/packages/sui-bundler/webpack.config.prod.js +++ b/packages/sui-bundler/webpack.config.prod.js @@ -13,7 +13,7 @@ const {when, cleanList, envVars, MAIN_ENTRY_POINT, config} = require('./shared/i const {aliasFromConfig} = require('./shared/resolve-alias.js') const {extractComments, sourceMap, supportLegacyBrowsers, cacheDirectory} = require('./shared/config.js') const {resolveLoader} = require('./shared/resolve-loader.js') -const createBabelRules = require('./shared/module-rules-babel.js') +const createCompilerRules = require('./shared/module-rules-compiler.js') const sassRules = require('./shared/module-rules-sass.js') const definePlugin = require('./shared/define.js') const manifestLoaderRules = require('./shared/module-rules-manifest-loader.js') @@ -41,7 +41,7 @@ const webpackConfig = { context: path.resolve(CWD, 'src'), resolve: { alias: {...aliasFromConfig}, - extensions: ['.js', '.json'], + extensions: ['.js', '.json', '.ts', '.tsx'], modules: ['node_modules', path.resolve(CWD)], fallback: { assert: false, @@ -74,7 +74,7 @@ const webpackConfig = { }, plugins: cleanList([ new webpack.ProvidePlugin({ - process: 'process/browser' + process: 'process/browser.js' }), new webpack.ids.HashedModuleIdsPlugin(), new webpack.EnvironmentPlugin(envVars(config.env)), @@ -104,7 +104,7 @@ const webpackConfig = { ]), module: { rules: cleanList([ - createBabelRules({supportLegacyBrowsers}), + createCompilerRules({supportLegacyBrowsers}), sassRules, when(config['externals-manifest'], () => manifestLoaderRules(config['externals-manifest'])) ]) diff --git a/packages/sui-bundler/webpack.config.server.js b/packages/sui-bundler/webpack.config.server.js index 515b4515b..3230f3f98 100644 --- a/packages/sui-bundler/webpack.config.server.js +++ b/packages/sui-bundler/webpack.config.server.js @@ -4,7 +4,7 @@ const path = require('path') const {config, when, cleanList} = require('./shared/index.js') const {cacheDirectory} = require('./shared/config.js') -const createBabelRules = require('./shared/module-rules-babel.js') +const createCompilerRules = require('./shared/module-rules-compiler.js') const manifestLoaderRules = require('./shared/module-rules-manifest-loader.js') const {aliasFromConfig} = require('./shared/resolve-alias.js') const {resolveLoader} = require('./shared/resolve-loader.js') @@ -22,7 +22,7 @@ const webpackConfig = { mode: isProduction ? 'production' : 'development', resolve: { alias: {...aliasFromConfig}, - extensions: ['.js', '.json'], + extensions: ['.js', '.json', '.ts', '.tsx'], modules: ['node_modules', path.resolve(process.cwd())] }, entry: './server.js', @@ -48,7 +48,7 @@ const webpackConfig = { resolveLoader, module: { rules: cleanList([ - createBabelRules({isServer: true}), + createCompilerRules({isServer: true}), { // ignore css/scss/svg require/imports files in the server test: /(\.svg|\.s?css)$/, diff --git a/packages/sui-compiler-config/package.json b/packages/sui-compiler-config/package.json new file mode 100644 index 000000000..24cdef292 --- /dev/null +++ b/packages/sui-compiler-config/package.json @@ -0,0 +1,22 @@ +{ + "name": "@s-ui/compiler-config", + "version": "1.0.0", + "description": "Compilers config (SWC, TS...) for SUI projects.", + "main": "src/index.js", + "scripts": {}, + "keywords": [], + "author": "", + "license": "MIT", + "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-compiler-config#readme", + "dependencies": { + "@tsconfig/esm": "1.0.4", + "@tsconfig/vite-react": "2.0.0" + } +} diff --git a/packages/sui-js-compiler/swc-config.js b/packages/sui-compiler-config/src/index.js similarity index 82% rename from packages/sui-js-compiler/swc-config.js rename to packages/sui-compiler-config/src/index.js index ff493c407..204b465ec 100644 --- a/packages/sui-js-compiler/swc-config.js +++ b/packages/sui-compiler-config/src/index.js @@ -10,9 +10,10 @@ const DEFAULT_BROWSER_TARGETS = { ios: '14.5' } -export const getSWCConfig = ({isModern, isTypeScript}) => { +const getSWCConfig = ({isModern = false, isTypeScript = false, compileToCJS = false}) => { const targets = isModern ? DEFAULT_BROWSER_TARGETS : DEFAULT_LEGACY_BROWSER_TARGETS const syntaxOptions = isTypeScript ? {syntax: 'typescript', tsx: true} : {syntax: 'ecmascript', jsx: true} + const moduleOptions = compileToCJS ? {module: {type: 'commonjs'}} : {} return { minify: true, @@ -39,6 +40,7 @@ export const getSWCConfig = ({isModern, isTypeScript}) => { loose: true, externalHelpers: true }, + ...moduleOptions, env: { targets, dynamicImport: true, @@ -48,3 +50,5 @@ export const getSWCConfig = ({isModern, isTypeScript}) => { } } } + +module.exports.getSWCConfig = getSWCConfig diff --git a/packages/sui-compiler-config/tsconfig.json b/packages/sui-compiler-config/tsconfig.json new file mode 100644 index 000000000..09340ea34 --- /dev/null +++ b/packages/sui-compiler-config/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": ["@tsconfig/esm/tsconfig.json", "@tsconfig/vite-react/tsconfig.json"], + "compilerOptions": { + "allowJs": true, + "target": "ES2022" + } +} + diff --git a/packages/sui-consents/package.json b/packages/sui-consents/package.json index a37e72155..74ce96328 100644 --- a/packages/sui-consents/package.json +++ b/packages/sui-consents/package.json @@ -5,13 +5,14 @@ "description": "iab consents handler", "types": "lib/index", "scripts": { - "lib": "rm -rf ./lib && tsc", + "lib": "sui-js-compiler", "prepublishOnly": "npm run lib" }, "keywords": [], "author": "", "license": "MIT", "devDependencies": { + "@s-ui/js-compiler": "1", "@s-ui/react-context": "1", "@testing-library/react": "11.2.5", "react": "17", diff --git a/packages/sui-consents/src/hasUserConsents.ts b/packages/sui-consents/src/hasUserConsents.ts index 5929b6a8e..f2b9cd116 100644 --- a/packages/sui-consents/src/hasUserConsents.ts +++ b/packages/sui-consents/src/hasUserConsents.ts @@ -1,5 +1,5 @@ import atob from './atob' -import { TCF_COOKIE_KEY } from './config' +import {TCF_COOKIE_KEY} from './config' /** * Extracts the cookie value from the cookie string @@ -10,13 +10,13 @@ const readCookie = (key: string, cookies: string): string | null => { return value !== null ? value[1] : null } -const getUserConsents = ({ cookies }: { cookies: string}): object => { +const getUserConsents = ({cookies}: {cookies: string}): object => { const cookieValue = readCookie(TCF_COOKIE_KEY, cookies) if (cookieValue === null) return {} try { - const { purpose } = JSON.parse(atob(cookieValue)) - const { consents } = purpose + const {purpose} = JSON.parse(atob(cookieValue)) + const {consents} = purpose return consents } catch (e) { // eslint-disable-next-line no-console @@ -25,9 +25,13 @@ const getUserConsents = ({ cookies }: { cookies: string}): object => { } } -export default function hasUserConsents ( - { requiredConsents, cookies }: { requiredConsents: number[], cookies: string} -): boolean { - const userConsents = getUserConsents({ cookies }) +export default function hasUserConsents({ + requiredConsents, + cookies +}: { + requiredConsents: number[] + cookies: string +}): boolean { + const userConsents = getUserConsents({cookies}) return requiredConsents.every(purposeId => Boolean(userConsents[purposeId])) } diff --git a/packages/sui-consents/src/index.ts b/packages/sui-consents/src/index.ts index e24fe31b2..409e0d857 100644 --- a/packages/sui-consents/src/index.ts +++ b/packages/sui-consents/src/index.ts @@ -1,4 +1,4 @@ import hasUserConsents from './hasUserConsents' import useUserConsents from './useUserConsents' -export { hasUserConsents, useUserConsents } +export {hasUserConsents, useUserConsents} diff --git a/packages/sui-consents/src/types.ts b/packages/sui-consents/src/types.ts index 3c8cf68ca..198687f51 100644 --- a/packages/sui-consents/src/types.ts +++ b/packages/sui-consents/src/types.ts @@ -1,18 +1,6 @@ export interface Purpose { - consents: { - /** - * true - Consent - * false - No Consent. - */ - [purposeId: number]: boolean | undefined - } - legitimateInterests: { - /** - * true - Legitimate Interest Established, - * false - No Legitimate Interest Established - */ - [purposeId: number]: boolean - } + consents: Record + legitimateInterests: Record } export enum EventStatus { @@ -50,5 +38,5 @@ export enum EventStatus { * in accordance with TCF Policy and a CMP is prepared to respond to any * calling scripts with the corresponding TC String. */ - USER_ACTION_COMPLETE = 'useractioncomplete', + USER_ACTION_COMPLETE = 'useractioncomplete' } diff --git a/packages/sui-consents/src/useUserConsents.ts b/packages/sui-consents/src/useUserConsents.ts index c4a70d0e3..01facd2b6 100644 --- a/packages/sui-consents/src/useUserConsents.ts +++ b/packages/sui-consents/src/useUserConsents.ts @@ -1,21 +1,19 @@ -import { useContext, useEffect, useState } from 'react' +import {useContext, useEffect, useState} from 'react' import SUIContext from '@s-ui/react-context' -import { TCF_VERSION, TCF_WINDOW_API } from './config' +import {TCF_VERSION, TCF_WINDOW_API} from './config' import hasUserConsents from './hasUserConsents' -import { EventStatus, Purpose } from './types' +import {type Purpose, EventStatus} from './types' -export default function useUserConsents (requiredConsents: number[]): boolean { +export default function useUserConsents(requiredConsents: number[]): boolean { /** * Consents acceptance state is inited based on the cookies from the * context, so we know the state of consents from the beginning, even * in SSR. */ - const { cookies } = useContext(SUIContext) - const [areConsentsAccepted, setAreConsentsAccepted] = useState(() => - hasUserConsents({ requiredConsents, cookies }) - ) + const {cookies}: {cookies: string} = useContext(SUIContext) + const [areConsentsAccepted, setAreConsentsAccepted] = useState(() => hasUserConsents({requiredConsents, cookies})) /** * From then on, we listen for TCF events so consents changes @@ -25,14 +23,10 @@ export default function useUserConsents (requiredConsents: number[]): boolean { useEffect(() => { const tcfApi = window[TCF_WINDOW_API] if (tcfApi !== undefined) { - const consentsListener = ({ eventStatus, purpose }: { eventStatus: EventStatus, purpose: Purpose}): void => { + const consentsListener = ({eventStatus, purpose}: {eventStatus: EventStatus; purpose: Purpose}): void => { if (eventStatus !== EventStatus.USER_ACTION_COMPLETE) return - setAreConsentsAccepted( - requiredConsents.every(purposeId => - Boolean(purpose.consents[purposeId]) - ) - ) + setAreConsentsAccepted(requiredConsents.every(purposeId => Boolean(purpose.consents[purposeId]))) } tcfApi('addEventListener', TCF_VERSION, consentsListener) return () => { diff --git a/packages/sui-consents/test/common/functionSpec.js b/packages/sui-consents/test/common/functionSpec.js index ba6dec449..8fc1b87dc 100644 --- a/packages/sui-consents/test/common/functionSpec.js +++ b/packages/sui-consents/test/common/functionSpec.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ import {expect} from 'chai' -import {hasUserConsents} from '../../lib/index' +import {hasUserConsents} from '../../lib/index.js' describe('hasUserConsents', () => { describe('when the user accepts all consents', () => { diff --git a/packages/sui-consents/test/common/index.js b/packages/sui-consents/test/common/index.js index 99bb219c6..e9c5905f5 100644 --- a/packages/sui-consents/test/common/index.js +++ b/packages/sui-consents/test/common/index.js @@ -1,2 +1,2 @@ -import './functionSpec' -import './hookSpec' +import './functionSpec.js' +import './hookSpec.js' diff --git a/packages/sui-decorators/package.json b/packages/sui-decorators/package.json index 24df5bdaf..02dc788b9 100644 --- a/packages/sui-decorators/package.json +++ b/packages/sui-decorators/package.json @@ -4,13 +4,14 @@ "description": "> Set of ES6 decorators to improve your apps", "main": "lib/", "scripts": { - "lib": "babel --presets sui ./src --out-dir ./lib", + "lib": "sui-js-compiler", "prepublishOnly": "npm run lib" }, "keywords": [], "author": "", "license": "MIT", "devDependencies": { + "@s-ui/js-compiler": "1", "sinon": "10.0.0" }, "browser": { diff --git a/packages/sui-domain/package.json b/packages/sui-domain/package.json index 1df396fe2..8dca96d0f 100644 --- a/packages/sui-domain/package.json +++ b/packages/sui-domain/package.json @@ -5,7 +5,7 @@ "description": "SDK for creating domains", "main": "lib/index.js", "scripts": { - "lib": "babel --presets sui ./src --out-dir ./lib", + "lib": "sui-js-compiler", "prepublishOnly": "npm run lib", "test": "npm run test:server && npm run test:browser", "test:browser": "NODE_ENV=test sui-test browser -P 'test/browser/*Spec.js'", @@ -16,6 +16,9 @@ "keywords": [], "author": "", "license": "MIT", + "devDependencies": { + "@s-ui/js-compiler": "1" + }, "dependencies": { "axios": "1.6.7" } diff --git a/packages/sui-domain/src/fetcher/InterceptableFetcherInterface.d.ts b/packages/sui-domain/src/fetcher/InterceptableFetcherInterface.d.ts index d50551989..9bd073ddd 100644 --- a/packages/sui-domain/src/fetcher/InterceptableFetcherInterface.d.ts +++ b/packages/sui-domain/src/fetcher/InterceptableFetcherInterface.d.ts @@ -1,6 +1,6 @@ /** @typedef {import('./FetcherInterface').default} FetcherInterface */ /** @extends {FetcherInterface} */ export default interface InterceptableFetcherInterface { - setErrorInterceptor: (callback: Function) => void + setErrorInterceptor: (callback: () => void) => void unsetErrorInterceptor: () => void } diff --git a/packages/sui-helpers/packages.js b/packages/sui-helpers/packages.js index ad8c8d1df..2c9b2d4e0 100644 --- a/packages/sui-helpers/packages.js +++ b/packages/sui-helpers/packages.js @@ -1,6 +1,6 @@ /* eslint no-console:0 */ const path = require('path') -const {getSpawnPromise} = require('./cli') +const {getSpawnPromise} = require('./cli.js') /** * Get absolute paths of packages diff --git a/packages/sui-hoc/package.json b/packages/sui-hoc/package.json index f2d3dab18..c655d2112 100644 --- a/packages/sui-hoc/package.json +++ b/packages/sui-hoc/package.json @@ -3,9 +3,8 @@ "version": "1.36.0", "description": "Set of HoC useful for react", "main": "lib/index.js", - "type": "module", "scripts": { - "lib": "babel --presets sui ./src --out-dir ./lib", + "lib": "sui-js-compiler", "prepublishOnly": "npm run lib" }, "peerDependencies": { @@ -13,6 +12,9 @@ "prop-types": "15", "react": "16 || 17" }, + "devDependencies": { + "@s-ui/js-compiler": "1" + }, "dependencies": { "intersection-observer": "0.10.0" } diff --git a/packages/sui-js-compiler/index.js b/packages/sui-js-compiler/index.js index 5afddbaee..cdf725efb 100755 --- a/packages/sui-js-compiler/index.js +++ b/packages/sui-js-compiler/index.js @@ -10,9 +10,7 @@ import path from 'node:path' import {transformFile} from '@swc/core' -import {getSpawnPromise} from '@s-ui/helpers/cli.js' - -import {getSWCConfig} from './swc-config.js' +import {getSWCConfig} from '@s-ui/compiler-config' const SOURCE_DIR = './src' const OUTPUT_DIR = './lib' @@ -34,20 +32,6 @@ const DEFAULT_TS_CONFIG = { types: ['react', 'node'] } -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') @@ -72,7 +56,7 @@ const compileFile = async (file, options) => { } const compileTypes = async (files, options) => { - const {createCompilerHost, createProgram} = await dynamicPackage('typescript') + const {createCompilerHost, createProgram} = await import('typescript').then(module => module.default) const createdFiles = {} const host = createCompilerHost(options) host.writeFile = (fileName, contents) => (createdFiles[fileName] = contents) @@ -108,6 +92,8 @@ const {ignore: ignoreOpts = [], modern: isModern = false} = program.opts() const ignore = [...ignoreOpts, '**/__tests__'] ;(async () => { + console.time('[sui-js-compiler]') + const files = await fg('./src/**/*.{js,jsx,ts,tsx}', {ignore}) const filesToCompile = Promise.all( files.map(async file => { @@ -127,4 +113,6 @@ const ignore = [...ignoreOpts, '**/__tests__'] : Promise.resolve() await Promise.all([filesToCompile, typesToCompile]) + + console.timeEnd('[sui-js-compiler]') })() diff --git a/packages/sui-js-compiler/package.json b/packages/sui-js-compiler/package.json index fb125e563..b4b7fd639 100644 --- a/packages/sui-js-compiler/package.json +++ b/packages/sui-js-compiler/package.json @@ -12,11 +12,12 @@ }, "license": "MIT", "dependencies": { - "@s-ui/helpers": "1", + "@s-ui/compiler-config": "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" + "fs-extra": "10.1.0", + "typescript": "5.0.4" } } diff --git a/packages/sui-js-compiler/test/server/jsCompilerSpec.js b/packages/sui-js-compiler/test/server/jsCompilerSpec.js index 1e5eac42a..873ebe881 100644 --- a/packages/sui-js-compiler/test/server/jsCompilerSpec.js +++ b/packages/sui-js-compiler/test/server/jsCompilerSpec.js @@ -9,7 +9,6 @@ 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)) @@ -21,11 +20,6 @@ describe('@s-ui/js-compiler', () => { await fs.remove(tsConfigPath) }) - afterEach(async () => { - await fs.remove(libPath) - await fs.remove(tsConfigPath) - }) - it('should compile a "src" folder with a JavaScript file and output it to "lib"', async () => { const {stdout} = await exec('node ../../index.js', { cwd @@ -34,14 +28,12 @@ describe('@s-ui/js-compiler', () => { const compiledFilenames = await fs.readdir(libPath) expect(compiledFilenames).to.eql(['example.js', 'example.test.js']) - expect(stdout).to.contain('[sui-js-compiler]') const compiledFile = await fs.readFile(libFilePath, 'utf-8') expect(compiledFile).to.contain('react/jsx-runtime') expect(compiledFile).to.contain('_jsx') - expect(compiledFile).to.contain('_async_to_generator') expect(compiledFile).to.contain('_ts_decorate') expect(compiledFile).to.contain('_ts_generator') @@ -55,14 +47,12 @@ describe('@s-ui/js-compiler', () => { const compiledFilenames = await fs.readdir(libPath) expect(compiledFilenames).to.eql(['example.js']) - expect(stdout).to.contain('[sui-js-compiler]') const compiledFile = await fs.readFile(libFilePath, 'utf-8') expect(compiledFile).to.contain('react/jsx-runtime') expect(compiledFile).to.contain('_jsx') - expect(compiledFile).to.contain('_async_to_generator') expect(compiledFile).to.contain('_ts_decorate') expect(compiledFile).to.contain('_ts_generator') diff --git a/packages/sui-js-compiler/test/server/src/example.tsx b/packages/sui-js-compiler/test/server/src/example.tsx index d7e209472..e91998d4b 100644 --- a/packages/sui-js-compiler/test/server/src/example.tsx +++ b/packages/sui-js-compiler/test/server/src/example.tsx @@ -4,6 +4,6 @@ interface ThingProps { type?: 'inert' | 'moving' } -export default function Thing({name, type = 'moving'}: ThingProps): React.FC { +export default function Thing({name, type = 'moving'}: ThingProps): React.ReactElement { return
{name}
} diff --git a/packages/sui-lint/eslintrc.js b/packages/sui-lint/eslintrc.js index 02ee6aa3c..c9b234d77 100644 --- a/packages/sui-lint/eslintrc.js +++ b/packages/sui-lint/eslintrc.js @@ -211,14 +211,21 @@ module.exports = { // }, { files: ['**/*.+(ts|tsx)'], - extends: ['standard-with-typescript'], + extends: ['standard-with-typescript', 'standard-react', 'prettier'], + parser: '@typescript-eslint/parser', parserOptions: { project: './tsconfig.json' }, rules: { + 'import/extensions': RULES.OFF, 'no-return-await': RULES.OFF, - 'prettier/prettier': RULES.OFF, - 'react/react-in-jsx-scope': RULES.OFF + 'prettier/prettier': [RULES.ERROR, prettierOptions], + 'react/react-in-jsx-scope': RULES.OFF, + 'react/no-unused-prop-types': RULES.OFF, + '@typescript-eslint/explicit-function-return-type': [RULES.OFF, {allowTypedFunctionExpressions: false}], + 'chai-friendly/no-unused-expressions': RULES.ERROR, + '@typescript-eslint/no-unused-expressions': RULES.OFF, + '@typescript-eslint/return-await': RULES.OFF } }, { diff --git a/packages/sui-lint/package.json b/packages/sui-lint/package.json index 053c305ff..49f02dbc3 100644 --- a/packages/sui-lint/package.json +++ b/packages/sui-lint/package.json @@ -24,7 +24,7 @@ "eslint": "8.56.0", "eslint-config-prettier": "8.5.0", "eslint-config-standard": "17.0.0", - "eslint-config-standard-react": "^13.0.0", + "eslint-config-standard-react": "13.0.0", "eslint-config-standard-with-typescript": "22.0.0", "eslint-plugin-chai-friendly": "0.7.2", "eslint-plugin-cypress": "2.12.1", diff --git a/packages/sui-mock/package.json b/packages/sui-mock/package.json index ebbc78279..5733ca469 100644 --- a/packages/sui-mock/package.json +++ b/packages/sui-mock/package.json @@ -2,12 +2,11 @@ "name": "@s-ui/mock", "version": "1.5.0", "main": "lib/index.js", - "description": "mock provider", - "types": "lib/index", + "description": "Mock provider", "scripts": { - "lib": "babel --presets sui ./src --out-dir ./lib", + "lib": "sui-js-compiler", "prepublishOnly": "npm run lib", - "test": "npm run test:client && npm run test:server", + "test": "npm run test:browser && npm run test:server", "test:browser": "npx @s-ui/test browser -P './test/browser/**/*Spec.js'", "test:browser:watch": "npm run test:client -- --watch", "test:server": "npx @s-ui/test server -P './test/server/**/*Spec.js'", @@ -16,8 +15,11 @@ "keywords": [], "author": "", "license": "MIT", + "devDependencies": { + "@s-ui/js-compiler": "1" + }, "dependencies": { - "msw": "0.47.4" + "msw": "1.2.1" }, "repository": { "type": "git", diff --git a/packages/sui-mock/src/browser.js b/packages/sui-mock/src/browser.js index 6dca6c6dc..1923d7de2 100644 --- a/packages/sui-mock/src/browser.js +++ b/packages/sui-mock/src/browser.js @@ -5,8 +5,13 @@ export const getBrowserMocker = async (handlers = []) => { const worker = setup(...handlers) return { - ...worker, - listen: worker.start, - close: worker.stop + start: worker.start.bind(worker), + stop: worker.stop.bind(worker), + listen: worker.start.bind(worker), + close: worker.stop.bind(worker), + use: worker.use.bind(worker), + resetHandlers: worker.resetHandlers.bind(worker), + restoreHandlers: worker.restoreHandlers.bind(worker), + printHandlers: worker.printHandlers.bind(worker) } } diff --git a/packages/sui-mock/src/server.js b/packages/sui-mock/src/server.js index a1ca38c4a..0461c967b 100644 --- a/packages/sui-mock/src/server.js +++ b/packages/sui-mock/src/server.js @@ -1,12 +1,17 @@ export {rest} from 'msw' export const getServerMocker = async (handlers = []) => { - const {setupServer} = require('msw/node') + const setupServer = await import('msw/node').then(pkg => pkg.setupServer) const worker = setupServer(...handlers) return { - ...worker, - start: worker.listen, - stop: worker.close + start: worker.listen.bind(worker), + stop: worker.close.bind(worker), + listen: worker.listen.bind(worker), + close: worker.close.bind(worker), + use: worker.use.bind(worker), + resetHandlers: worker.resetHandlers.bind(worker), + restoreHandlers: worker.restoreHandlers.bind(worker), + printHandlers: worker.printHandlers.bind(worker) } } diff --git a/packages/sui-react-context/src/index.tsx b/packages/sui-react-context/src/index.tsx index 0fea4a34b..2c1582a97 100644 --- a/packages/sui-react-context/src/index.tsx +++ b/packages/sui-react-context/src/index.tsx @@ -13,12 +13,12 @@ SUIContext.wrapper = (Component, displayName): React.ComponentType => { {context => } ) - WrappedComponent.displayName = Component.displayName !== undefined ? Component.displayName : displayName + WrappedComponent.displayName = Component.displayName ?? displayName return hoistNonReactStatics(WrappedComponent, Component) } -export const useSuiContext = (): React.Context => { +export function useSuiContext(): React.Context { return React.useContext(SUIContext) } diff --git a/packages/sui-react-head/package.json b/packages/sui-react-head/package.json index 9133600e9..46589942b 100644 --- a/packages/sui-react-head/package.json +++ b/packages/sui-react-head/package.json @@ -5,7 +5,7 @@ "main": "lib/index.js", "types": "lib/index.d.ts", "scripts": { - "lib": "rm -rf ./lib && tsc", + "lib": "sui-js-compiler", "prepublishOnly": "npm run lib" }, "keywords": [], @@ -15,6 +15,7 @@ "react-head": "3.4.0" }, "devDependencies": { + "@s-ui/js-compiler": "1", "@testing-library/react": "11.1.0", "react": "17", "react-dom": "17" diff --git a/packages/sui-react-head/src/Body.tsx b/packages/sui-react-head/src/Body.tsx index e67ac54c3..dbf9f9aa0 100644 --- a/packages/sui-react-head/src/Body.tsx +++ b/packages/sui-react-head/src/Body.tsx @@ -1,7 +1,7 @@ /* eslint-disable react/prop-types */ -import { useEffect } from 'react' -import { Meta } from 'react-head' +import {useEffect} from 'react' +import {Meta} from 'react-head' export const BODY_ATTRIBUTES_KEY = 'bodyattributes' const isClient = typeof window !== 'undefined' @@ -10,24 +10,24 @@ interface BodyProps { attributes: object } -const Body: React.FC = ({ attributes = {} }) => { +const Body: React.FC = ({attributes = {}}) => { useEffect(() => { - const { body } = document + const {body} = document - function toggleBodyAttributes ({ action = 'set' } = {}): void { + function toggleBodyAttributes({action = 'set'} = {}): void { const method = `${action}Attribute` - Object.entries(attributes).forEach(([key, value]) => - body[method](key, value) - ) + Object.entries(attributes).forEach(([key, value]) => body[method](key, value)) } toggleBodyAttributes() - return () => toggleBodyAttributes({ action: 'remove' }) + return () => { + toggleBodyAttributes({action: 'remove'}) + } }, [attributes]) if (isClient) return null // on the server, use the Meta tag to extract later - const metaProps = { ...attributes, name: BODY_ATTRIBUTES_KEY } + const metaProps = {...attributes, name: BODY_ATTRIBUTES_KEY} return } diff --git a/packages/sui-react-head/src/Html.tsx b/packages/sui-react-head/src/Html.tsx index 10cd69326..e9335f89d 100644 --- a/packages/sui-react-head/src/Html.tsx +++ b/packages/sui-react-head/src/Html.tsx @@ -1,6 +1,6 @@ /* eslint-disable react/prop-types */ -import { Meta } from 'react-head' +import {Meta} from 'react-head' export const HTML_ATTRIBUTES_KEY = 'htmlattributes' const isClient = typeof window !== 'undefined' @@ -9,10 +9,10 @@ interface HtmlProps { attributes: object } -const Html: React.FC = ({ attributes = {} }) => { +const Html: React.FC = ({attributes = {}}) => { if (isClient) return null // on the server, use the Meta tag to extract later - const metaProps = { ...attributes, name: HTML_ATTRIBUTES_KEY } + const metaProps = {...attributes, name: HTML_ATTRIBUTES_KEY} return } diff --git a/packages/sui-react-head/src/index.tsx b/packages/sui-react-head/src/index.tsx index f655ab2bf..99e0a0f80 100644 --- a/packages/sui-react-head/src/index.tsx +++ b/packages/sui-react-head/src/index.tsx @@ -1,13 +1,11 @@ /* eslint-disable react/prop-types */ import * as React from 'react' -import { - HeadProvider, Link, Meta as MetaPrimitive, Style, Title -} from 'react-head' +import {HeadProvider, Link, Meta as MetaPrimitive, Style, Title} from 'react-head' import Body from './Body' import Html from './Html' -import { extractTagsFrom, extractTitleFrom, renderStyles, renderTags } from './utils' +import {extractTagsFrom, extractTitleFrom, renderStyles, renderTags} from './utils' interface HeadProps { bodyAttributes?: object @@ -31,23 +29,16 @@ interface MetaTagInverterProps extends React.MetaHTMLAttributes 'data-rh': string } -const MetaTagInverter: React.FC = ({ 'data-rh': rh, ...others }) => { +const MetaTagInverter: React.FC = ({'data-rh': rh, ...others}) => { return } -const Meta: React.FC = (props) => { - // @ts-expect-error +const Meta: React.FC = props => { + // @ts-expect-error: We should expect any error return } -const Head: React.FC = ({ - bodyAttributes, - children, - htmlAttributes, - title, - meta = [], - link = [] -}) => { +const Head: React.FC = ({bodyAttributes, children, htmlAttributes, title, meta = [], link = []}) => { const metaTagsToRender = extractTagsFrom({ children, tag: 'meta', @@ -73,14 +64,14 @@ const Head: React.FC = ({ return ( <> {titleToRender !== '' && {titleToRender}} - {renderTags({ tagsArray: metaTagsToRender, Component: Meta })} - {renderTags({ tagsArray: linkTagsToRender, Component: Link })} - {renderStyles({ stylesArray: stylesTagsToRender, Component: Style })} + {renderTags({tagsArray: metaTagsToRender, Component: Meta})} + {renderTags({tagsArray: linkTagsToRender, Component: Link})} + {renderStyles({stylesArray: stylesTagsToRender, Component: Style})} {bodyAttributes != null && } {htmlAttributes != null && } ) } -export { HeadProvider } +export {HeadProvider} export default Head diff --git a/packages/sui-react-head/src/server.ts b/packages/sui-react-head/src/server.ts index 8bbfd0aaa..e5cd0f0e0 100644 --- a/packages/sui-react-head/src/server.ts +++ b/packages/sui-react-head/src/server.ts @@ -1,8 +1,8 @@ -import { renderToString } from 'react-dom/server' +import {renderToString} from 'react-dom/server' -import { BODY_ATTRIBUTES_KEY } from './Body' -import { HTML_ATTRIBUTES_KEY } from './Html' -import { ComponentTag } from './types' +import {BODY_ATTRIBUTES_KEY} from './Body' +import {HTML_ATTRIBUTES_KEY} from './Html' +import {type ComponentTag} from './types' interface ExtractPropsFromConfig { withKey: string @@ -12,13 +12,13 @@ interface ExtractPropsFromConfig { * Extract props from a list of tags a specific tag by using a key * and then discard this key before returning the key */ -const extractPropsFrom = (tags: ComponentTag[], { withKey }: ExtractPropsFromConfig): undefined | { [x: string]: any} => { +const extractPropsFrom = (tags: ComponentTag[], {withKey}: ExtractPropsFromConfig): undefined | Record => { // search the tag using the key and default to an empty object for simplicity - const tag: ComponentTag | undefined = tags.find(({ props }) => props.name === withKey) + const tag: ComponentTag | undefined = tags.find(({props}) => props.name === withKey) if (tag != null) { // discard the key used to search the tag - const { name, ...restOfTag } = tag.props + const {name, ...restOfTag} = tag.props // return only the desired info for the tag return restOfTag } @@ -27,7 +27,7 @@ const extractPropsFrom = (tags: ComponentTag[], { withKey }: ExtractPropsFromCon /** * Transform the object from the head to a string to be used in the server */ -const transformToString = (headObject: { [key: string]: string} = {}): string => +const transformToString = (headObject: Record = {}): string => Object.entries(headObject) .map(([key, value]) => `${key}="${value}"`) .join(' ') @@ -35,7 +35,11 @@ const transformToString = (headObject: { [key: string]: string} = {}): string => /** * Render the tags for the head */ -export function renderHeadTagsToString (headTags: any[]): { headString: string, bodyAttributes: string, htmlAttributes: string} { +export function renderHeadTagsToString(headTags: ComponentTag[]): { + headString: string + bodyAttributes: string + htmlAttributes: string +} { const bodyAttributesProps = extractPropsFrom(headTags, { withKey: BODY_ATTRIBUTES_KEY }) @@ -44,8 +48,7 @@ export function renderHeadTagsToString (headTags: any[]): { headString: string, }) const headTagsToRender = headTags.filter( - ({ props }) => - props.name !== BODY_ATTRIBUTES_KEY && props.name !== HTML_ATTRIBUTES_KEY + ({props}) => props.name !== BODY_ATTRIBUTES_KEY && props.name !== HTML_ATTRIBUTES_KEY ) return { diff --git a/packages/sui-react-head/src/utils.tsx b/packages/sui-react-head/src/utils.tsx index ddb6068cd..c9430237b 100644 --- a/packages/sui-react-head/src/utils.tsx +++ b/packages/sui-react-head/src/utils.tsx @@ -1,17 +1,16 @@ import * as React from 'react' -import { Children as ReactChildren } from 'react' +import {Children as ReactChildren} from 'react' -import { Style, Tag } from './types' +import {type Style, type Tag} from './types' -const checkRelNeedsHref = (rel: string): boolean => - ['alternate', 'preload', 'prefetch'].includes(rel) +const checkRelNeedsHref = (rel: string): boolean => ['alternate', 'preload', 'prefetch'].includes(rel) /** * Use rel as key but put extra info if needed * to avoid duplicated keys */ const extractRelAsKey = (tag: Tag): string | undefined => { - const { rel, href, hreflang } = tag + const {rel, href, hreflang} = tag if (rel != null) { if (hreflang != null) return `${rel}-${hreflang}` if (checkRelNeedsHref(rel) && href != null) return `${rel}-${href}` @@ -23,21 +22,21 @@ const extractRelAsKey = (tag: Tag): string | undefined => { * Extract value in a specific order */ const extractKeyFromTag = (tag: Tag): string | undefined => { - const { name, content } = tag + const {name, content} = tag return name ?? extractRelAsKey(tag) ?? content } /** * Extract children from React by tag type and return an array of React Type Component */ -export const extract = ({ children, byTag }: { children: React.ReactNode, byTag: string}): Tag[] => { +export const extract = ({children, byTag}: {children: React.ReactNode; byTag: string}): Tag[] => { const arrayOfComponents = ReactChildren.toArray(children) - return arrayOfComponents.filter(child => - React.isValidElement(child) && child.type === byTag - ).map(child => { - const el = child as React.ReactElement - return { ...el.props } - }) + return arrayOfComponents + .filter(child => React.isValidElement(child) && child.type === byTag) + .map(child => { + const el = child as React.ReactElement + return {...el.props} + }) } interface extractTagsFromParams { @@ -46,23 +45,27 @@ interface extractTagsFromParams { fallback?: any[] } -export const extractTagsFrom = ({ children, tag, fallback }: extractTagsFromParams): any[] => { +export const extractTagsFrom = ({children, tag, fallback}: extractTagsFromParams): any[] => { if (children != null) { - return extract({ children, byTag: tag }) + return extract({children, byTag: tag}) } - return (fallback != null) ? fallback : [] + return fallback ?? [] } -interface extractTitleFromParams { children: React.ReactNode, fallback?: string} +interface extractTitleFromParams { + children: React.ReactNode + fallback?: string +} -export const extractTitleFrom = ({ children, fallback = '' }: extractTitleFromParams): string => { +export const extractTitleFrom = ({children, fallback = ''}: extractTitleFromParams): string => { if (typeof children === 'undefined') return fallback - const listOfTitles = extract({ children, byTag: 'title' }) + const listOfTitles = extract({children, byTag: 'title'}) if (listOfTitles.length > 0) { const [title] = listOfTitles - return title.children !== undefined ? title.children : fallback + + return title.children ?? fallback } return fallback } @@ -74,16 +77,10 @@ interface renderTagsParams { tagsArray: any[] Component: React.ComponentType } -export const renderTags = ({ tagsArray = [], Component }: renderTagsParams): JSX.Element[] => +export const renderTags = ({tagsArray = [], Component}: renderTagsParams): JSX.Element[] => tagsArray.map((tag: Tag) => { - const { hreflang: hrefLang, ...restOfTagInfo } = tag - return ( - - ) + const {hreflang: hrefLang, ...restOfTagInfo} = tag + return }) /** @@ -93,8 +90,12 @@ interface renderStylesParams { stylesArray: any[] Component: React.ComponentType } -export const renderStyles = ({ stylesArray = [], Component }: renderStylesParams): JSX.Element[] => +export const renderStyles = ({stylesArray = [], Component}: renderStylesParams): JSX.Element[] => stylesArray.map((style: Style, index: number) => { - const { children, ...styleAttr } = style - return {children} + const {children, ...styleAttr} = style + return ( + + {children} + + ) }) diff --git a/packages/sui-react-head/test/server/serverSpec.js b/packages/sui-react-head/test/server/serverSpec.js index 86f2acd6c..d45f1585f 100644 --- a/packages/sui-react-head/test/server/serverSpec.js +++ b/packages/sui-react-head/test/server/serverSpec.js @@ -55,7 +55,7 @@ describe.server('react-head on server', () => { const {headString, bodyAttributes, htmlAttributes} = renderHeadTagsToString(headTags) expect(headString).to.equal( - 'My awesome title' + 'My awesome title' ) expect(htmlAttributes).to.equal('data-rh="" lang="es"') expect(bodyAttributes).to.equal('data-rh="" class="is-test"') @@ -81,7 +81,7 @@ describe.server('react-head on server', () => { const {headString, bodyAttributes, htmlAttributes} = renderHeadTagsToString(headTags) expect(headString).to.equal( - 'My awesome title' + 'My awesome title' ) expect(htmlAttributes).to.equal('data-rh="" lang="es"') expect(bodyAttributes).to.equal('data-rh="" class="is-test"') @@ -112,7 +112,7 @@ describe.server('react-head on server', () => { const {headString, bodyAttributes, htmlAttributes} = renderHeadTagsToString(headTags) expect(headString).to.equal( - 'My awesome title' + 'My awesome title' ) expect(htmlAttributes).to.equal('data-rh="" lang="es"') expect(bodyAttributes).to.equal('data-rh="" class="is-test"') diff --git a/packages/sui-react-initial-props/package.json b/packages/sui-react-initial-props/package.json index 785c3ad14..74f6d6920 100644 --- a/packages/sui-react-initial-props/package.json +++ b/packages/sui-react-initial-props/package.json @@ -5,7 +5,7 @@ "types": "lib/index.d.ts", "description": "Provide a way to get initial props for async pages", "scripts": { - "lib": "rm -rf ./lib && tsc", + "lib": "sui-js-compiler", "prepublishOnly": "npm run lib" }, "keywords": [], @@ -17,10 +17,11 @@ "react": "16 || 17" }, "devDependencies": { + "@s-ui/js-compiler": "1", + "@s-ui/react-router": "1", "@testing-library/react": "11.1.0", "@types/node": "16", "react": "17", - "react-dom": "17", - "@s-ui/react-router": "1" + "react-dom": "17" } } diff --git a/packages/sui-react-initial-props/src/createClientContextFactoryParams.ts b/packages/sui-react-initial-props/src/createClientContextFactoryParams.ts index 5f69702f7..389f098cd 100644 --- a/packages/sui-react-initial-props/src/createClientContextFactoryParams.ts +++ b/packages/sui-react-initial-props/src/createClientContextFactoryParams.ts @@ -1,4 +1,4 @@ -import { ContextFactoryParams } from './types' +import {type ContextFactoryParams} from './types' export default (): ContextFactoryParams => ({ appConfig: window.__APP_CONFIG__, diff --git a/packages/sui-react-initial-props/src/createServerContextFactoryParams.ts b/packages/sui-react-initial-props/src/createServerContextFactoryParams.ts index 5012976bf..fa437efcf 100644 --- a/packages/sui-react-initial-props/src/createServerContextFactoryParams.ts +++ b/packages/sui-react-initial-props/src/createServerContextFactoryParams.ts @@ -1,4 +1,4 @@ -import { ContextFactoryParams } from './types' +import {type ContextFactoryParams} from './types' export default (req: IncomingMessage.ServerRequest): ContextFactoryParams => ({ appConfig: req.appConfig, diff --git a/packages/sui-react-initial-props/src/index.ts b/packages/sui-react-initial-props/src/index.ts index f043a5c6d..ceeecb070 100644 --- a/packages/sui-react-initial-props/src/index.ts +++ b/packages/sui-react-initial-props/src/index.ts @@ -1,4 +1,4 @@ -export { default as createClientContextFactoryParams } from './createClientContextFactoryParams' -export { default as createServerContextFactoryParams } from './createServerContextFactoryParams' -export { default as loadPage } from './loadPage' -export { default as ssrComponentWithInitialProps } from './ssrComponentWithInitialProps' +export {default as createClientContextFactoryParams} from './createClientContextFactoryParams' +export {default as createServerContextFactoryParams} from './createServerContextFactoryParams' +export {default as loadPage} from './loadPage' +export {default as ssrComponentWithInitialProps} from './ssrComponentWithInitialProps' diff --git a/packages/sui-react-initial-props/src/initialPropsContext.ts b/packages/sui-react-initial-props/src/initialPropsContext.ts index 83d89f74f..b37eb74f3 100644 --- a/packages/sui-react-initial-props/src/initialPropsContext.ts +++ b/packages/sui-react-initial-props/src/initialPropsContext.ts @@ -1,3 +1,3 @@ -import { createContext } from 'react' +import {createContext} from 'react' -export default createContext({ initialProps: {} }) +export default createContext({initialProps: {}}) diff --git a/packages/sui-react-initial-props/src/loadPage.tsx b/packages/sui-react-initial-props/src/loadPage.tsx index 74599fc99..887335b08 100644 --- a/packages/sui-react-initial-props/src/loadPage.tsx +++ b/packages/sui-react-initial-props/src/loadPage.tsx @@ -1,41 +1,47 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { useContext } from 'react' +import {useContext} from 'react' import InitialPropsContext from './initialPropsContext' -import { ClientPageComponent, DoneImportingPageCallback, ReactRouterTypes, WithInitialPropsComponent } from './types' +import { + type ClientPageComponent, + type DoneImportingPageCallback, + type ReactRouterTypes, + type WithInitialPropsComponent +} from './types' import withInitialProps from './withInitialProps' const EMPTY_GET_INITIAL_PROPS = async (): Promise => ({}) -const createUniversalPage = (routeInfo: ReactRouterTypes.RouteInfo) => async ({ default: Page }: {default: ClientPageComponent}) => { - // check if the Page page has a getInitialProps, if not put a resolve with an empty object - Page.getInitialProps = - typeof Page.getInitialProps === 'function' - ? Page.getInitialProps - : EMPTY_GET_INITIAL_PROPS +const createUniversalPage = + (routeInfo: ReactRouterTypes.RouteInfo) => + async ({default: Page}: {default: ClientPageComponent}) => { + // check if the Page page has a getInitialProps, if not put a resolve with an empty object + Page.getInitialProps = typeof Page.getInitialProps === 'function' ? Page.getInitialProps : EMPTY_GET_INITIAL_PROPS - // CLIENT - if (typeof window !== 'undefined') { - // let withInitialProps HOC handle client getInitialProps logic - return await Promise.resolve(withInitialProps(Page)) + // CLIENT + if (typeof window !== 'undefined') { + // let withInitialProps HOC handle client getInitialProps logic + return Promise.resolve(withInitialProps(Page)) + } + // SERVER + // Create a component that gets the initialProps from context + // this context has been created on the `ssrWithComponentWithInitialProps` + const ServerPage: WithInitialPropsComponent = (props: object) => { + const {initialProps} = useContext(InitialPropsContext) + return + } + // recover the displayName from the original page + ServerPage.displayName = Page.displayName + // detect if the page has getInitialProps and wrap it with the routeInfo + // if we don't have any getInitialProps, just use a empty function returning an empty object + ServerPage.getInitialProps = async ( + context: object, + req: IncomingMessage.ServerRequest, + res: IncomingMessage.ClientResponse + ) => await Page.getInitialProps({context, routeInfo, req, res}) + // return the component to be used on the server + return ServerPage } - // SERVER - // Create a component that gets the initialProps from context - // this context has been created on the `ssrWithComponentWithInitialProps` - const ServerPage: WithInitialPropsComponent = (props: object) => { - const { initialProps } = useContext(InitialPropsContext) - return - } - // recover the displayName from the original page - ServerPage.displayName = Page.displayName - // detect if the page has getInitialProps and wrap it with the routeInfo - // if we don't have any getInitialProps, just use a empty function returning an empty object - ServerPage.getInitialProps = - async (context: object, req: IncomingMessage.ServerRequest, res: IncomingMessage.ClientResponse) => - await Page.getInitialProps({ context, routeInfo, req, res }) - // return the component to be used on the server - return ServerPage -} // TODO: Remove this method on next major as it's using unnecessary contextFactory param // and unnecesary calling done method instead relying on promises diff --git a/packages/sui-react-initial-props/src/ssrComponentWithInitialProps.tsx b/packages/sui-react-initial-props/src/ssrComponentWithInitialProps.tsx index f0ba75dac..8665c3807 100644 --- a/packages/sui-react-initial-props/src/ssrComponentWithInitialProps.tsx +++ b/packages/sui-react-initial-props/src/ssrComponentWithInitialProps.tsx @@ -1,11 +1,11 @@ -import { renderToNodeStream, renderToString } from 'react-dom/server' +import {renderToNodeStream, renderToString} from 'react-dom/server' import InitialPropsContext from './initialPropsContext' -import { InitialProps, SsrComponentWithInitialPropsParams } from './types' +import {type InitialProps, type SsrComponentWithInitialPropsParams} from './types' const hrTimeToMs = (diff: [number, number]): number => diff[0] * 1e3 + diff[1] * 1e-6 -export default async function ssrComponentWithInitialProps ({ +export default async function ssrComponentWithInitialProps({ Target, context, req, @@ -15,12 +15,11 @@ export default async function ssrComponentWithInitialProps ({ }: SsrComponentWithInitialPropsParams): Promise { const startGetInitialProps = process.hrtime() // use the getInitialProps from the page to retrieve the props to initialize - const { getInitialProps } = - renderProps.components[renderProps.components.length - 1] + const {getInitialProps} = renderProps.components[renderProps.components.length - 1] const initialProps: InitialProps = await getInitialProps(context, req, res) const diffGetInitialProps = process.hrtime(startGetInitialProps) - const { __HTTP__: http } = initialProps + const {__HTTP__: http} = initialProps if (http?.redirectTo !== undefined) { return { @@ -34,7 +33,7 @@ export default async function ssrComponentWithInitialProps ({ // Create App with Context with the initialProps const AppWithContext = ( - + ) @@ -45,7 +44,7 @@ export default async function ssrComponentWithInitialProps ({ // start to calculate renderToString const startRenderToString = process.hrtime() // render with the needed action - const renderResponse = { [renderResponseKey]: renderAction(AppWithContext) } + const renderResponse = {[renderResponseKey]: renderAction(AppWithContext)} // calculate the difference of time used rendering const diffRenderToString = process.hrtime(startRenderToString) // return all the info diff --git a/packages/sui-react-initial-props/src/types.d.ts b/packages/sui-react-initial-props/src/types.d.ts index d53677bb5..73e76e4ea 100644 --- a/packages/sui-react-initial-props/src/types.d.ts +++ b/packages/sui-react-initial-props/src/types.d.ts @@ -1,9 +1,7 @@ -import { RouteInfo } from '@s-ui/react-router/src/types' +import {type RouteInfo} from '@s-ui/react-router/src/types' export * as ReactRouterTypes from '@s-ui/react-router/src/types' -export interface InitialProps { - [key: string]: any -} +export type InitialProps = Record export interface PageComponentOptions { keepMounted?: boolean renderLoading?: (params: GetInitialPropsClientFunctionParams) => React.ElementType @@ -19,15 +17,14 @@ export interface ContextFactoryParams { req?: object } -export type ClientPageComponent = React.ComponentType -& PageComponentOptions -& { getInitialProps: GetInitialPropsFunction } +export type ClientPageComponent = React.ComponentType & + PageComponentOptions & {getInitialProps: GetInitialPropsFunction} -export type ServerPageComponent = React.ComponentType -& PageComponentOptions -& { getInitialProps: GetInitialPropsServerFunction } +export type ServerPageComponent = React.ComponentType & + PageComponentOptions & {getInitialProps: GetInitialPropsServerFunction} -export type GetInitialPropsServerFunction = (context: object, +export type GetInitialPropsServerFunction = ( + context: object, req: IncomingMessage.ServerRequest, res: IncomingMessage.ClientResponse ) => Promise @@ -39,11 +36,9 @@ export interface GetInitialPropsClientFunctionParams { res?: IncomingMessage.ClientResponse } -export type GetInitialPropsFunction = - (params: GetInitialPropsClientFunctionParams) => Promise +export type GetInitialPropsFunction = (params: GetInitialPropsClientFunctionParams) => Promise -export type DoneImportingPageCallback = - (err: null, Page: WithInitialPropsComponent) => Promise +export type DoneImportingPageCallback = (err: null, Page: WithInitialPropsComponent) => Promise export interface RenderProps { components: ServerPageComponent[] diff --git a/packages/sui-react-initial-props/src/withInitialProps.tsx b/packages/sui-react-initial-props/src/withInitialProps.tsx index d4a5f4849..f1d44a734 100644 --- a/packages/sui-react-initial-props/src/withInitialProps.tsx +++ b/packages/sui-react-initial-props/src/withInitialProps.tsx @@ -1,13 +1,9 @@ -import { useContext, useEffect, useRef, useState } from 'react' +import {useContext, useEffect, useRef, useState} from 'react' import SUIContext from '@s-ui/react-context' -import { RouteInfo } from '@s-ui/react-router/src/types' +import {type RouteInfo} from '@s-ui/react-router/src/types' -import { - ClientPageComponent, - InitialProps, - WithInitialPropsComponent -} from './types' +import {type ClientPageComponent, type InitialProps, type WithInitialPropsComponent} from './types' const INITIAL_PROPS_KEY = '__INITIAL_PROPS__' @@ -22,7 +18,7 @@ const getInitialPropsFromWindow = (): object | undefined => { } // extract needed info from props for routeInfo object -const createRouteInfoFromProps = ({ location, params, routes }: RouteInfo): RouteInfo => ({ +const createRouteInfoFromProps = ({location, params, routes}: RouteInfo): RouteInfo => ({ location, params, routes @@ -39,7 +35,7 @@ const createRouteInfoFromProps = ({ location, params, routes }: RouteInfo): Rout // gets props updated. Also, since PageComponent keeps mounted it will receive // an `isLoading` prop while getInitialProps is in progress. export default (Page: ClientPageComponent): WithInitialPropsComponent => { - const { keepMounted = false } = Page + const {keepMounted = false} = Page // gather window initial props for this Page, if present const windowInitialProps = getInitialPropsFromWindow() // remove the variable of the window @@ -55,9 +51,9 @@ export default (Page: ClientPageComponent): WithInitialPropsComponent => { // consume sui context from the context provider const suiContext: object = useContext(SUIContext) // pathName from context is outdated, so we update it from routeInfo - const context = { ...suiContext, pathName: routeInfo.location.pathname } + const context = {...suiContext, pathName: routeInfo.location.pathname} - const [{ initialProps, isLoading }, setState] = useState(() => ({ + const [{initialProps, isLoading}, setState] = useState(() => ({ initialProps: initialPropsFromWindowRef.current ?? {}, isLoading: initialPropsFromWindowRef.current == null })) @@ -70,22 +66,22 @@ export default (Page: ClientPageComponent): WithInitialPropsComponent => { } else { // only update state if already request initial props if (requestedInitialPropsOnceRef.current) { - setState({ initialProps, isLoading: true }) + setState({initialProps, isLoading: true}) } - Page.getInitialProps({ context, routeInfo }) + Page.getInitialProps({context, routeInfo}) .then((initialProps: InitialProps) => { - const { __HTTP__: http } = initialProps + const {__HTTP__: http} = initialProps if (http?.redirectTo !== undefined) { window.location = http.redirectTo return } - setState({ initialProps, isLoading: false }) + setState({initialProps, isLoading: false}) }) .catch((error: Error) => { - setState({ initialProps: { error }, isLoading: false }) + setState({initialProps: {error}, isLoading: false}) }) .finally(() => { if (requestedInitialPropsOnceRef.current) return @@ -94,9 +90,7 @@ export default (Page: ClientPageComponent): WithInitialPropsComponent => { } }, [routeInfo.location]) // eslint-disable-line react-hooks/exhaustive-deps - const renderPage = (): any => ( - - ) + const renderPage = (): any => // if the page has a `keepMounted` property and already requested // initialProps once, just keep rendering the page @@ -106,9 +100,7 @@ export default (Page: ClientPageComponent): WithInitialPropsComponent => { const renderLoading = (): React.ElementType | null => { // check if the page has a `renderLoading` method, if not, just render nothing - return (Page.renderLoading != null) - ? Page.renderLoading({ context, routeInfo }) - : null + return Page.renderLoading != null ? Page.renderLoading({context, routeInfo}) : null } return isLoading ? renderLoading() : renderPage() @@ -116,10 +108,7 @@ export default (Page: ClientPageComponent): WithInitialPropsComponent => { // if `keepMounted` property is found and the component is the same one, // we just reuse it instead of returning a new one - if ( - keepMounted && - Page.displayName === latestClientPage?.Page?.displayName - ) { + if (keepMounted && Page.displayName === latestClientPage?.Page?.displayName) { return latestClientPage } diff --git a/packages/sui-react-initial-props/tsconfig.json b/packages/sui-react-initial-props/tsconfig.json index e7bd7dc4f..89de39364 100644 --- a/packages/sui-react-initial-props/tsconfig.json +++ b/packages/sui-react-initial-props/tsconfig.json @@ -3,5 +3,6 @@ "compilerOptions": { "outDir": "./lib", "rootDir": "./src" - } -} \ No newline at end of file + }, + "exclude": ["./lib"] +} diff --git a/packages/sui-studio/bin/sui-studio-test.js b/packages/sui-studio/bin/sui-studio-test.js index 9f0397aec..b810bbfa8 100755 --- a/packages/sui-studio/bin/sui-studio-test.js +++ b/packages/sui-studio/bin/sui-studio-test.js @@ -25,6 +25,11 @@ program const {coverage, watch, ci, headless, timeout} = program.opts() +const relPath = path.relative( + process.cwd(), + require.resolve('@s-ui/studio/src/runtime-mocha/index.js').replace(/\/node_modules.*/, '') +) + const run = async () => { try { const result = await serialSpawn([ @@ -32,7 +37,7 @@ const run = async () => { suiTestClientPath, [ '--pattern', - path.join('node_modules', '@s-ui', 'studio', 'src', 'runtime-mocha', 'index.js'), + path.join(relPath, 'node_modules', '@s-ui', 'studio', 'src', 'runtime-mocha', 'index.js'), coverage && '--coverage', watch && '--watch', ci && '--ci', diff --git a/packages/sui-studio/bin/sui-studio.js b/packages/sui-studio/bin/sui-studio.js index 97ea1363c..2f28b0024 100755 --- a/packages/sui-studio/bin/sui-studio.js +++ b/packages/sui-studio/bin/sui-studio.js @@ -10,6 +10,33 @@ const copyGlobals = require('./helpers/copyGlobals.js') program.version(version, ' --version') +const listOfComponents = () => { + const fg = require('fast-glob') + const path = require('path') + + const folders = fg.sync('components/*/*', { + deep: 3, + onlyDirectories: true + }) + const components = folders.map(folder => { + const [, category, component] = folder.split(path.sep) + return {category, component} + }) + return components +} + +const config = () => { + const path = require('path') + const {config = {}} = require(path.join(process.cwd(), 'package.json')) + console.log(path.join(process.cwd(), 'package.json')) + return config['sui-studio'] || {} +} + +process.env.MAGIC_STRINGS = JSON.stringify({ + __SUI_STUDIO_COMPONENTS_LIST__: listOfComponents(), + __SUI_STUDIO_CONFIG__: config() +}) + program .command('start') .alias('s') @@ -22,7 +49,8 @@ program const devServerExec = require.resolve('@s-ui/bundler/bin/sui-bundler-dev') const args = ['-c', path.join(__dirname, '..', 'src')] getSpawnPromise(devServerExec, args, { - shell: false + shell: false, + env: process.env }).then(process.exit, process.exit) }) diff --git a/packages/sui-studio/src/components/globals.js b/packages/sui-studio/src/components/globals.js index 7be636942..cace2bba1 100644 --- a/packages/sui-studio/src/components/globals.js +++ b/packages/sui-studio/src/components/globals.js @@ -3,14 +3,11 @@ import {safeImport} from './utils.js' export const importGlobals = () => { - // we use a variable for the file so Webpack - // could safe fail if the file doesn't exist - const globalsFile = 'globals.js' return safeImport({ importFile: () => import( /* webpackInclude: /\/components\/globals.js$/ */ - `${__BASE_DIR__}/components/${globalsFile}` + `${__BASE_DIR__}/components/globals.js` ) }) } diff --git a/packages/sui-studio/src/components/preview/_style.scss b/packages/sui-studio/src/components/preview/_style.scss index 6c76cbd9c..258cf00f6 100644 --- a/packages/sui-studio/src/components/preview/_style.scss +++ b/packages/sui-studio/src/components/preview/_style.scss @@ -68,8 +68,7 @@ } &:focus { outline: none; - box-shadow: inset 0 0 0 1px $c-primary, - 0 0 0 4px rgba($c-primary, 0.2); + box-shadow: inset 0 0 0 1px $c-primary, 0 0 0 4px rgba($c-primary, 0.2); } } & + button { @@ -134,8 +133,7 @@ } &:focus { outline: none; - box-shadow: inset 0 0 0 1px $c-primary, - 0 0 0 4px rgba($c-primary, 0.2); + box-shadow: inset 0 0 0 1px $c-primary, 0 0 0 4px rgba($c-primary, 0.2); } } } diff --git a/packages/sui-studio/src/components/tryRequire.js b/packages/sui-studio/src/components/tryRequire.js index 7ef5815e7..7ec60d96e 100644 --- a/packages/sui-studio/src/components/tryRequire.js +++ b/packages/sui-studio/src/components/tryRequire.js @@ -1,6 +1,6 @@ /* global __BASE_DIR__ */ -import {safeImport} from './utils' +import {safeImport} from './utils.js' const fetchStaticFile = path => window @@ -54,8 +54,8 @@ export const importReactComponent = ({category, component, subComponentName = nu importFile: () => { return import( /* webpackChunkName: "src-[request]" */ - /* webpackExclude: /\/node_modules\/(.*)\/src\/index.js$/ */ - `${__BASE_DIR__}/components/${category}/${component}/src/index.js` + /* webpackExclude: /\/node_modules\/(.*)\/src\/index$/ */ + `${__BASE_DIR__}/components/${category}/${component}/src/index` ) } }) @@ -66,20 +66,21 @@ const importDemo = ({category, component}) => importFile: () => import( /* webpackChunkName: "demo-[request]" */ - /* webpackExclude: /\/node_modules\/(.*)\/demo\/index.js$/ */ - `${__BASE_DIR__}/components/${category}/${component}/demo/index.js` + /* webpackExclude: /\/node_modules\/(.*)\/demo\/index$/ */ + `${__BASE_DIR__}/components/${category}/${component}/demo/index` ) }) export const importGlobals = () => { // we use a variable for the file so Webpack // could safe fail if the file doesn't exist - const globalsFile = 'globals.js' + // const globalsFile = 'globals.js' return safeImport({ importFile: () => import( /* webpackInclude: /\/components\/globals.js$/ */ - `${__BASE_DIR__}/components/${globalsFile}` + /* webpackExclude: /(.*)\.md$/ */ + `${__BASE_DIR__}/components/globals.js` ) }) } diff --git a/packages/sui-studio/src/components/utils.js b/packages/sui-studio/src/components/utils.js index d19f8d960..bf7df0828 100644 --- a/packages/sui-studio/src/components/utils.js +++ b/packages/sui-studio/src/components/utils.js @@ -1,3 +1,4 @@ +/* globals __SUI_STUDIO_CONFIG__ __SUI_STUDIO_COMPONENTS_LIST__ */ import {forwardRef} from 'react' import hoistNonReactStatics from 'hoist-non-react-statics' @@ -8,27 +9,11 @@ const DEFAULT_LOGO = " " export const getComponentsList = () => { - const listOfComponents = preval` - const fg = require('fast-glob') - const path = require('path') - - const folders = fg.sync('components/*/*', { deep: 3, onlyDirectories: true}) - const components = folders.map(folder => { - const [,category, component] = folder.split(path.sep) - return {category, component} - }) - module.exports = components - ` - return listOfComponents + return __SUI_STUDIO_COMPONENTS_LIST__ } export const getSuiStudioConfig = () => { - const config = preval` - const path = require('path') - const {config = {}} = require(path.join(process.cwd(), 'package.json')) - module.exports = config['sui-studio'] || {} - ` - return config + return __SUI_STUDIO_CONFIG__ } export const getStudioName = () => { diff --git a/packages/sui-studio/src/runtime-mocha/index.js b/packages/sui-studio/src/runtime-mocha/index.js index 3bd12a255..50406e766 100644 --- a/packages/sui-studio/src/runtime-mocha/index.js +++ b/packages/sui-studio/src/runtime-mocha/index.js @@ -1,4 +1,5 @@ /* global __BASE_DIR__, CATEGORIES, PATTERN */ +/* eslint-disable no-console */ /** * This file is being executed in browser opened to run tests @@ -13,7 +14,7 @@ addSetupEnvironment(window) window.__STUDIO_CONTEXTS__ = {} window.__STUDIO_COMPONENT__ = {} -const defaultPattern = '**/*.test.{js,jsx}' +const defaultPattern = '**/*.test.{js,jsx,ts,tsx}' const globPattern = PATTERN || defaultPattern const categories = CATEGORIES ? CATEGORIES.split(',') : null @@ -33,7 +34,7 @@ window.__karma__.loaded = () => {} const testsFiles = require.context( `${__BASE_DIR__}/components/`, true, - /\.\/(\w+)\/(\w+)\/test\/(components\.)?(\w+).test.(js|jsx)/ + /\.\/(\w+)\/(\w+)\/test\/(components\.)?(\w+).test.(js|jsx|ts|tsx)/ ) const selectedTestFiles = testsFiles.keys().filter(filterAll) diff --git a/packages/sui-studio/src/styles/_settings.scss b/packages/sui-studio/src/styles/_settings.scss index 63c0d4189..57a732d2d 100644 --- a/packages/sui-studio/src/styles/_settings.scss +++ b/packages/sui-studio/src/styles/_settings.scss @@ -23,8 +23,8 @@ $bgc-tag: #eeeeee; // --- Fonts --- // // Font family -$ff-sans-serif: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, - Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol' !default; +$ff-sans-serif: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol' !default; // Font weights $fw-ultra-light: 100 !default; diff --git a/packages/sui-studio/workbench/src/components/Root/index.js b/packages/sui-studio/workbench/src/components/Root/index.js index 37f324270..e2b09aea6 100644 --- a/packages/sui-studio/workbench/src/components/Root/index.js +++ b/packages/sui-studio/workbench/src/components/Root/index.js @@ -2,12 +2,12 @@ import {useState} from 'react' import PropTypes from 'prop-types' -import Header from '../Header/index.js' -import Select from '../Select/index.js' -import Test from '../Suite/index.js' +import Header from '../Header/index' +import Select from '../Select/index' +import Test from '../Suite/index' -const importComponent = () => import('component/index.js') -const importTest = () => import('test/index.test.js') +const importComponent = () => import('component/index') +const importTest = () => import('test/index.test') const getFromStorage = (key, defaultValue) => window.sessionStorage[key] || defaultValue diff --git a/packages/sui-studio/workbench/src/components/Root/index.scss b/packages/sui-studio/workbench/src/components/Root/index.scss index f9e2c8aa7..cf7b0a5d0 100644 --- a/packages/sui-studio/workbench/src/components/Root/index.scss +++ b/packages/sui-studio/workbench/src/components/Root/index.scss @@ -1,6 +1,6 @@ * { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, - Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', + 'Helvetica Neue', sans-serif; } .Root { diff --git a/packages/sui-test-contract/package.json b/packages/sui-test-contract/package.json index 3aa3a1cac..7abbaabb5 100644 --- a/packages/sui-test-contract/package.json +++ b/packages/sui-test-contract/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@pact-foundation/pact": "9.18.1", - "@pactflow/pact-msw-adapter": "1.2.1", + "@pactflow/pact-msw-adapter": "2.0.0", "@s-ui/mock": "1", "commander": "8.3.0", "headers-polyfill": "3.1.2" diff --git a/packages/sui-test-contract/src/setup/utils.js b/packages/sui-test-contract/src/setup/utils.js index 4b90f57a0..0eed3f01b 100644 --- a/packages/sui-test-contract/src/setup/utils.js +++ b/packages/sui-test-contract/src/setup/utils.js @@ -1,6 +1,6 @@ import {stringify} from 'qs' -import {writeData2File} from '@pactflow/pact-msw-adapter/dist/utils/utils.js' +import {createWriter} from '@pactflow/pact-msw-adapter/dist/utils/utils.js' const flatEntries = (input, prefix = '') => Object.entries(input).flatMap(([key, value]) => { @@ -52,7 +52,7 @@ export const writerFactory = providers => (path, data) => { }) console.log(`Writing the Pact file "${path}"`) // eslint-disable-line - writeData2File(path, data) + createWriter()(path, data) } export const mapProviders = providers => diff --git a/packages/sui-test-contract/test/server/setupSpec.js b/packages/sui-test-contract/test/server/setupSpec.js index 0baef5f19..958cc6243 100644 --- a/packages/sui-test-contract/test/server/setupSpec.js +++ b/packages/sui-test-contract/test/server/setupSpec.js @@ -1,11 +1,12 @@ import {expect} from 'chai' import {FetcherFactory} from '@s-ui/domain' -import {rest} from '@s-ui/mock' +import mock from '@s-ui/mock' import {setupContractTests} from '../../src/index.js' import {getContractFileData, removeContractFiles} from '../utils.js' +const {rest} = mock const fetcher = FetcherFactory.httpFetcher({config: {}}) const consumer = 'test-consumer' const fujiAppleResponse = {color: 'red', type: 'Fuji'} diff --git a/packages/sui-test/bin/karma/config.js b/packages/sui-test/bin/karma/config.js index db52a9772..ac5f5f19a 100644 --- a/packages/sui-test/bin/karma/config.js +++ b/packages/sui-test/bin/karma/config.js @@ -3,7 +3,8 @@ const webpack = require('webpack') const path = require('path') const {envVars} = require('@s-ui/bundler/shared/index.js') -const {bundlerConfig, clientConfig, isWorkspace} = require('../../src/config.js') +const {getSWCConfig} = require('@s-ui/compiler-config') +const {bundlerConfig, clientConfig, isWorkspace, isInnerPackage} = require('../../src/config.js') const {captureConsole = true} = clientConfig const {sep} = path @@ -13,20 +14,17 @@ const {sep} = path * Where the value is always an empty string. */ const environmentVariables = envVars(bundlerConfig.env) -const prefix = isWorkspace() ? '../' : './' +const standardPrefix = isWorkspace() ? '../' : './' +const prefix = isInnerPackage() ? '../../' : standardPrefix const pwd = process.env.PWD - +const swcConfig = getSWCConfig({isTypeScript: true}) const config = { singleRun: true, - basePath: '', - frameworks: ['mocha', 'webpack'], - proxies: { '/mockServiceWorker.js': `/base/public/mockServiceWorker.js` }, - plugins: [ require.resolve('karma-webpack'), require.resolve('karma-chrome-launcher'), @@ -35,17 +33,12 @@ const config = { require.resolve('karma-coverage'), require.resolve('karma-spec-reporter') ], - reporters: ['spec'], - browsers: ['Chrome'], - browserDisconnectTolerance: 1, - webpackMiddleware: { stats: 'errors-only' }, - webpack: { devtool: 'eval', stats: 'errors-only', @@ -53,10 +46,11 @@ const config = { alias: { 'react/jsx-dev-runtime': path.resolve(pwd, prefix, 'node_modules/react/jsx-dev-runtime.js'), 'react/jsx-runtime': path.resolve(pwd, prefix, 'node_modules/react/jsx-runtime.js'), - '@s-ui/react-context': path.resolve(path.join(pwd, prefix, 'node_modules/@s-ui/react-context')) + '@s-ui/react-context': path.resolve(path.join(pwd, prefix, 'node_modules/@s-ui/react-context')), + 'studio-utils': path.resolve(path.join(pwd, prefix, 'packages/ui/studio-utils')) }, modules: [path.resolve(process.cwd()), 'node_modules'], - extensions: ['.mjs', '.js', '.jsx', '.json'], + extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'], fallback: { assert: false, child_process: false, @@ -103,6 +97,17 @@ const config = { dataUrl: () => '' } }, + { + test: /\.tsx?$/, + exclude: new RegExp(`node_modules(?!${sep}@s-ui${sep}studio${sep}src)`), + use: { + loader: 'swc-loader', + options: { + sync: true, + ...swcConfig + } + } + }, { test: /\.jsx?$/, exclude: new RegExp(`node_modules(?!${sep}@s-ui${sep}studio${sep}src)`), @@ -137,7 +142,6 @@ const config = { ] } }, - client: { captureConsole, mocha: { diff --git a/packages/sui-test/bin/mocha/applyEsmOverride.js b/packages/sui-test/bin/mocha/applyEsmOverride.js index dae621fd8..4535094cd 100644 --- a/packages/sui-test/bin/mocha/applyEsmOverride.js +++ b/packages/sui-test/bin/mocha/applyEsmOverride.js @@ -1,3 +1,4 @@ -const _module = require('module') -const fs = require('fs') +const _module = require('node:module') +const fs = require('node:fs') + _module.Module._extensions['.js'] = (module, filename) => module._compile(fs.readFileSync(filename, 'utf8'), filename) diff --git a/packages/sui-test/bin/mocha/register.js b/packages/sui-test/bin/mocha/register.js index e656d041a..f9ae51d14 100644 --- a/packages/sui-test/bin/mocha/register.js +++ b/packages/sui-test/bin/mocha/register.js @@ -1,6 +1,7 @@ const {serverConfig} = require('../../src/config.js') -const {forceTranspilation = [], esmOverride = false, useLibDir = false} = serverConfig +const {getSWCConfig} = require('@s-ui/compiler-config') +const {forceTranspilation = [], esmOverride = false, useLibDir = false} = serverConfig const regexToAdd = forceTranspilation.map(regexString => new RegExp(regexString)) if (esmOverride) { @@ -9,10 +10,20 @@ if (esmOverride) { const libDir = /lib/ const paths = [/@babel\/runtime/, /@s-ui/, /@adv-ui/, /mocks/, /src/, /test/, libDir, ...regexToAdd] +const only = useLibDir ? paths : paths.filter(path => path !== libDir) +const swcConfig = getSWCConfig({isTypeScript: true, compileToCJS: true}) + +// Register TS source files +require('@swc/register')({ + ignore: [/(.*)\.(js|cjs|mjs)/], + only, + ...swcConfig +}) +// Register JS source files require('@babel/register')({ - ignore: [], - only: useLibDir ? paths : paths.filter(path => path !== libDir), + ignore: [/(.*)\.ts/], + only, presets: [ [ 'babel-preset-sui', diff --git a/packages/sui-test/bin/sui-test-browser.js b/packages/sui-test/bin/sui-test-browser.js index 005995a2e..af1b6b733 100755 --- a/packages/sui-test/bin/sui-test-browser.js +++ b/packages/sui-test/bin/sui-test-browser.js @@ -9,7 +9,7 @@ program .option('-W, --watch', 'Run in watch mode') .option('-C, --ci', 'Run a Firefox headless for CI testing') .option('-H, --headless', 'Run a headless browser for testing') - .option('-P, --pattern ', 'Path pattern to include', 'test/**/*Spec.js') + .option('-P, --pattern ', 'Path pattern to include', 'test/**/*Spec.+(js|jsx|ts|tsx)') .option('-I, --ignore-pattern ', 'Path pattern to ignore for testing', false) .option('--src-pattern ', 'Define the source directory', false) .option('-T, --timeout ', 'Timeout', 2000) diff --git a/packages/sui-test/bin/sui-test-server.js b/packages/sui-test/bin/sui-test-server.js index cf7a207fb..a90e26470 100755 --- a/packages/sui-test/bin/sui-test-server.js +++ b/packages/sui-test/bin/sui-test-server.js @@ -31,8 +31,9 @@ serialSpawn([ require.resolve('mocha/bin/mocha.js'), [ path.join(process.cwd(), path.sep, pattern), - `--require ${path.join(__dirname, 'mocha', 'register.js')}`, '--recursive', + '--extension js,ts,cjs,mjs', + `--require ${path.join(__dirname, 'mocha', 'register.js')}`, inspect && '--inspect-brk', watch && '--watch', timeout && `--timeout ${timeout}`, diff --git a/packages/sui-test/package.json b/packages/sui-test/package.json index ee5571396..b5f099b99 100644 --- a/packages/sui-test/package.json +++ b/packages/sui-test/package.json @@ -23,7 +23,10 @@ "@babel/plugin-transform-modules-commonjs": "7.18.6", "@babel/register": "7.18.9", "@faker-js/faker": "8.0.2", + "@s-ui/compiler-config": "1", "@s-ui/helpers": "1", + "@swc/core": "1.3.14", + "@swc/register": "0.1.10", "babel-loader": "8.3.0", "babel-plugin-dynamic-import-node": "2.3.3", "babel-plugin-istanbul": "6.0.0", @@ -41,6 +44,7 @@ "mocha": "10.0.0", "process": "0.11.10", "stream-browserify": "3.0.0", + "swc-loader": "0.2.3", "tty-browserify": "0.0.1", "util": "0.12.4", "webpack": "5.82.1" diff --git a/packages/sui-test/src/config.js b/packages/sui-test/src/config.js index 53142e3fe..9f5c978a2 100644 --- a/packages/sui-test/src/config.js +++ b/packages/sui-test/src/config.js @@ -7,9 +7,14 @@ function isWorkspace() { return !workspaces && !isPrivate } +function isInnerPackage() { + return isWorkspace() && Boolean(process.cwd().match(/(.*)\/packages\/(.*)/)) +} + module.exports = { bundlerConfig, clientConfig, serverConfig, - isWorkspace + isWorkspace, + isInnerPackage } diff --git a/packages/sui-theme/src/components/_button.scss b/packages/sui-theme/src/components/_button.scss index 789766615..3e1fb1cb5 100644 --- a/packages/sui-theme/src/components/_button.scss +++ b/packages/sui-theme/src/components/_button.scss @@ -44,12 +44,7 @@ @include sui-button--flat; } @if $type == 'custom' { - @include sui-button--custom( - $background-color, - $color, - $icon-fill, - $icon-stroke - ); + @include sui-button--custom($background-color, $color, $icon-fill, $icon-stroke); } @if $size == 'small' { diff --git a/packages/sui-theme/src/components/atom/input/_settings.scss b/packages/sui-theme/src/components/atom/input/_settings.scss index ecfc7318b..58c472490 100644 --- a/packages/sui-theme/src/components/atom/input/_settings.scss +++ b/packages/sui-theme/src/components/atom/input/_settings.scss @@ -30,7 +30,6 @@ $c-atom-input--success: $c-success !default; $c-atom-input--error: $c-error !default; $c-atom-input--alert: $c-alert !default; -$sizes-atom-input: xl $h-atom-input--xl, l $h-atom-input--l, m $h-atom-input--m, - xs $h-atom-input--xs, s $h-atom-input--s !default; -$states-atom-input: success $c-atom-input--success, error $c-atom-input--error, - alert $c-atom-input--alert !default; +$sizes-atom-input: xl $h-atom-input--xl, l $h-atom-input--l, m $h-atom-input--m, xs $h-atom-input--xs, + s $h-atom-input--s !default; +$states-atom-input: success $c-atom-input--success, error $c-atom-input--error, alert $c-atom-input--alert !default; diff --git a/packages/sui-theme/src/settings-compat-v7/_layout.scss b/packages/sui-theme/src/settings-compat-v7/_layout.scss index 93727be1f..65979dc0b 100644 --- a/packages/sui-theme/src/settings-compat-v7/_layout.scss +++ b/packages/sui-theme/src/settings-compat-v7/_layout.scss @@ -26,11 +26,7 @@ $breakpoint-names: map-keys($breakpoints) !default; // md @function breakpoint-next($name) { $n: index($breakpoint-names, $name); - @return if( - $n < length($breakpoint-names), - nth($breakpoint-names, $n + 1), - null - ); + @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null); } // Maximum breakpoint width. Null for the largest (last) breakpoint. diff --git a/packages/sui-theme/src/utils/_colors.scss b/packages/sui-theme/src/utils/_colors.scss index d0b298e14..edb4ca3c2 100644 --- a/packages/sui-theme/src/utils/_colors.scss +++ b/packages/sui-theme/src/utils/_colors.scss @@ -18,12 +18,7 @@ $color-variation-light: white !default; $color-lighten-steps: 25, 50, 75, 90, 95 !default; $color-darken-steps: 20, 35, 55, 75 !default; -@function color-variation( - $color, - $step: 0, - $dark: $color-variation-dark, - $light: $color-variation-light -) { +@function color-variation($color, $step: 0, $dark: $color-variation-dark, $light: $color-variation-light) { $positive-steps: length($color-lighten-steps); $negative-steps: length($color-darken-steps); diff --git a/packages/sui-theme/src/utils/_text.scss b/packages/sui-theme/src/utils/_text.scss index 2f041512d..806068219 100644 --- a/packages/sui-theme/src/utils/_text.scss +++ b/packages/sui-theme/src/utils/_text.scss @@ -51,22 +51,12 @@ @include calc-font-size($fz-caption, $fw-caption, $ls-caption, $lh-caption); } @if $type == 'overline' { - @include calc-font-size( - $fz-overline, - $fw-overline, - $ls-overline, - $lh-overline - ); + @include calc-font-size($fz-overline, $fw-overline, $ls-overline, $lh-overline); } } @mixin font-size-large { - @include calc-font-size( - $fz-large-title, - $fw-large-title, - $ls-large-title, - $lh-large-title - ); + @include calc-font-size($fz-large-title, $fw-large-title, $ls-large-title, $lh-large-title); } @mixin font-size-title1 { @include calc-font-size($fz-title1, $fw-title1, $ls-title1, $lh-title1); @@ -78,20 +68,10 @@ @include calc-font-size($fz-title3, $fw-title3, $ls-title3, $lh-title3); } @mixin font-size-headline1 { - @include calc-font-size( - $fz-headline1, - $fw-headline1, - $ls-headline1, - $lh-headline1 - ); + @include calc-font-size($fz-headline1, $fw-headline1, $ls-headline1, $lh-headline1); } @mixin font-size-headline2 { - @include calc-font-size( - $fz-headline2, - $fw-headline2, - $ls-headline2, - $lh-headline2 - ); + @include calc-font-size($fz-headline2, $fw-headline2, $ls-headline2, $lh-headline2); } // CSS image replacement diff --git a/scripts/publish-tagged-packages.mjs b/scripts/publish-tagged-packages.mjs new file mode 100644 index 000000000..9ce60de9d --- /dev/null +++ b/scripts/publish-tagged-packages.mjs @@ -0,0 +1,108 @@ +#!/usr/bin/env node +/* eslint-disable no-console */ + +import {createRequire} from 'module' + +import {program} from 'commander' +import {$} from 'execa' +import fs from 'node:fs' +import path from 'node:path' +import prettier from 'prettier' + +const PACKAGE_REGEX = /packages\/((([a-z]+)-?)+)/ // matches "packages/sui-any-package-name" + +program + .name('publish-tagged-packages') + .description('CLI to publish new tagged versions from modified packages in pull requests.') + .version('0.0.1') + +program + .option('-t, --tag [tag]', 'Tag used to publish the packages to NPM.') + .option('-f, --files [files]', 'Comma-separated list of added and modified files.') + .on('--help', () => { + console.log(' Examples:') + console.log('') + console.log( + ' $ node ./scripts/publish-tagged-packages.mjs --tag \'ongoing-branch\' --files \'"packages/sui-mono/foo.js","packages/sui-bundler/bar.js"\'' + ) + console.log('') + }) + .parse(process.argv) + +function getConfig(fileName) { + const require = createRequire(import.meta.url) + + return require(`../${fileName}/package.json`) +} + +async function getPackageVersion({name, tag}) { + let version = '' + + try { + // Try to get a previously tagged version. + const {stdout: currentTaggedVersion} = await $`npm view ${name}@${tag} version` + const [mainVersion, taggedVersion] = currentTaggedVersion.split(`-${tag}.`) + + version = `${mainVersion}-${tag}.${Number(taggedVersion) + 1}` + } catch (error) { + // If not, create a new tag from the main package version. + const {stdout: currentVersion} = await $`npm view ${name} version` + const [major, minor] = currentVersion.split('.') + + version = `${major}.${Number(minor) + 1}.0-${tag}.0` + } + + return version +} + +async function publishPackages() { + const {tag, files = ''} = program.opts() + + if (!tag) { + throw new Error('๐Ÿคจ Tag is mandatory in order to publish new package versions.') + } + + // Get needed packages to publish. + const packagesToPublish = [ + // Avoid duplicates. + ...new Set( + files + .split(',') + // Filter only packages. + .filter(file => file.match(PACKAGE_REGEX)) + // Return the package path. + .map(file => file.match(PACKAGE_REGEX)[0]) + ) + ] + + if (!packagesToPublish.length) { + console.log('\n๐Ÿ’ There is no new tags to be published.\n') + return + } + + packagesToPublish.forEach(async packageName => { + const packageConfig = getConfig(packageName) + const {name} = packageConfig + const version = await getPackageVersion({name, tag}) + + // Set the new tagged version. + packageConfig.version = version + + const packagePath = path.join(process.cwd(), packageName) + const packageJsonPath = `${packagePath}/package.json` + const packageJson = await prettier.format(JSON.stringify(packageConfig), {parser: 'json'}) + + // Allow `execa` to execute NPM commands on the cwd of each package. + const $$ = $({cwd: packagePath}) + + console.log(`\n๐Ÿ“ฆ Publishing new tagged version: ${name}@${version}\n`) + + fs.writeFileSync(packageJsonPath, packageJson) + + await $$`npm publish --tag ${tag}` + }) +} + +await publishPackages() + +program.parse(process.argv) diff --git a/tsconfig.json b/tsconfig.json index d90b1d3a1..96482e01e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,11 +11,10 @@ "skipLibCheck": true, "strict": true, "target": "es5", - "types": [ - "react", - "node" - ] + "types": ["react", "node"] }, "exclude": ["node_modules", "lib"], - "typeAcquisition": { "enable": true } -} \ No newline at end of file + "typeAcquisition": { + "enable": true + } +}