From e60f79e47d54323aec4e87df23f4c281e6878318 Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Mon, 13 May 2024 12:48:51 +0100 Subject: [PATCH] Try `@guardian/source` package once more (#1435) Co-authored-by: Oliver Abrahams Co-authored-by: Alex Sanders --- .changeset/popular-pumas-draw.md | 5 + Makefile | 40 ++ README.md | 14 + apps/storybooks/.storybook/main.js | 5 + configs/rollup.config.js | 29 +- libs/@guardian/source/.eslintrc.cjs | 58 ++ libs/@guardian/source/.storybook/main.js | 22 + libs/@guardian/source/.storybook/manager.ts | 6 + .../source/.storybook/preview-head.html | 386 +++++++++++++ libs/@guardian/source/.storybook/preview.tsx | 23 + .../preview/FocusManagerDecorator.tsx | 11 + .../preview/ThemeProviderDecorator.tsx | 13 + .../source/.storybook/preview/backgrounds.ts | 27 + .../source/.storybook/preview/viewport.ts | 87 +++ libs/@guardian/source/.storybook/theme.ts | 9 + libs/@guardian/source/README.md | 39 ++ libs/@guardian/source/assets/logo.png | Bin 0 -> 11116 bytes .../@guardian/source/foundations/package.json | 6 + libs/@guardian/source/jest.config.js | 16 + libs/@guardian/source/jest.dist.setup.js | 9 + .../lib/jest-matchers/@types/index.d.ts | 13 + .../source/lib/jest-matchers/toBeValidCSS.ts | 41 ++ .../source/lib/jest-matchers/toMatchCSS.ts | 59 ++ libs/@guardian/source/package.json | 135 +++++ .../source/react-components/package.json | 6 + libs/@guardian/source/rollup.config.js | 25 + .../source/scripts/build-type-presets.ts | 69 +++ .../source/scripts/create-icons/README.md | 34 ++ .../create-icons/create-icon-component.ts | 192 +++++++ .../scripts/create-icons/create-readme.ts | 15 + .../create-icons/get-svgs-from-figma.ts | 88 +++ .../source/scripts/create-icons/index.ts | 89 +++ libs/@guardian/source/src/foundations | 1 + libs/@guardian/source/src/react-components | 1 + libs/@guardian/source/tsconfig.json | 19 + libs/@guardian/source/tsconfig.spec.json | 21 + pnpm-lock.yaml | 510 +++++------------- pnpm-workspace.yaml | 1 + 38 files changed, 1727 insertions(+), 397 deletions(-) create mode 100644 .changeset/popular-pumas-draw.md create mode 100644 libs/@guardian/source/.eslintrc.cjs create mode 100644 libs/@guardian/source/.storybook/main.js create mode 100644 libs/@guardian/source/.storybook/manager.ts create mode 100644 libs/@guardian/source/.storybook/preview-head.html create mode 100644 libs/@guardian/source/.storybook/preview.tsx create mode 100644 libs/@guardian/source/.storybook/preview/FocusManagerDecorator.tsx create mode 100644 libs/@guardian/source/.storybook/preview/ThemeProviderDecorator.tsx create mode 100644 libs/@guardian/source/.storybook/preview/backgrounds.ts create mode 100644 libs/@guardian/source/.storybook/preview/viewport.ts create mode 100644 libs/@guardian/source/.storybook/theme.ts create mode 100644 libs/@guardian/source/README.md create mode 100644 libs/@guardian/source/assets/logo.png create mode 100644 libs/@guardian/source/foundations/package.json create mode 100644 libs/@guardian/source/jest.config.js create mode 100644 libs/@guardian/source/jest.dist.setup.js create mode 100644 libs/@guardian/source/lib/jest-matchers/@types/index.d.ts create mode 100644 libs/@guardian/source/lib/jest-matchers/toBeValidCSS.ts create mode 100644 libs/@guardian/source/lib/jest-matchers/toMatchCSS.ts create mode 100644 libs/@guardian/source/package.json create mode 100644 libs/@guardian/source/react-components/package.json create mode 100644 libs/@guardian/source/rollup.config.js create mode 100644 libs/@guardian/source/scripts/build-type-presets.ts create mode 100644 libs/@guardian/source/scripts/create-icons/README.md create mode 100644 libs/@guardian/source/scripts/create-icons/create-icon-component.ts create mode 100644 libs/@guardian/source/scripts/create-icons/create-readme.ts create mode 100644 libs/@guardian/source/scripts/create-icons/get-svgs-from-figma.ts create mode 100644 libs/@guardian/source/scripts/create-icons/index.ts create mode 120000 libs/@guardian/source/src/foundations create mode 120000 libs/@guardian/source/src/react-components create mode 100644 libs/@guardian/source/tsconfig.json create mode 100644 libs/@guardian/source/tsconfig.spec.json diff --git a/.changeset/popular-pumas-draw.md b/.changeset/popular-pumas-draw.md new file mode 100644 index 000000000..1e3315a20 --- /dev/null +++ b/.changeset/popular-pumas-draw.md @@ -0,0 +1,5 @@ +--- +'@guardian/source': minor +--- + +third attempt diff --git a/Makefile b/Makefile index a2b853462..9ec41f182 100644 --- a/Makefile +++ b/Makefile @@ -402,6 +402,46 @@ install: check-node-version @guardian/prettier\:lint: env @corepack pnpm nx run @guardian/prettier:lint --skip-nx-cache=$(SKIP_NX_CACHE) +.PHONY: @guardian/source\:build +@guardian/source\:build: env + @corepack pnpm nx run @guardian/source:build --skip-nx-cache=$(SKIP_NX_CACHE) + +.PHONY: @guardian/source\:build-storybook +@guardian/source\:build-storybook: env + @corepack pnpm nx run @guardian/source:build-storybook --skip-nx-cache=$(SKIP_NX_CACHE) + +.PHONY: @guardian/source\:build-type-presets +@guardian/source\:build-type-presets: env + @corepack pnpm nx run @guardian/source:build-type-presets --skip-nx-cache=$(SKIP_NX_CACHE) + +.PHONY: @guardian/source\:create-icons +@guardian/source\:create-icons: env + @corepack pnpm nx run @guardian/source:create-icons --skip-nx-cache=$(SKIP_NX_CACHE) + +.PHONY: @guardian/source\:dev +@guardian/source\:dev: env + @corepack pnpm nx run @guardian/source:dev --skip-nx-cache=$(SKIP_NX_CACHE) + +.PHONY: @guardian/source\:fix +@guardian/source\:fix: env + @corepack pnpm nx run @guardian/source:fix --skip-nx-cache=$(SKIP_NX_CACHE) + +.PHONY: @guardian/source\:lint +@guardian/source\:lint: env + @corepack pnpm nx run @guardian/source:lint --skip-nx-cache=$(SKIP_NX_CACHE) + +.PHONY: @guardian/source\:storybook +@guardian/source\:storybook: env + @corepack pnpm nx run @guardian/source:storybook --skip-nx-cache=$(SKIP_NX_CACHE) + +.PHONY: @guardian/source\:test +@guardian/source\:test: env + @corepack pnpm nx run @guardian/source:test --skip-nx-cache=$(SKIP_NX_CACHE) + +.PHONY: @guardian/source\:verify-dist +@guardian/source\:verify-dist: env + @corepack pnpm nx run @guardian/source:verify-dist --skip-nx-cache=$(SKIP_NX_CACHE) + .PHONY: @guardian/source-foundations\:build @guardian/source-foundations\:build: env @corepack pnpm nx run @guardian/source-foundations:build --skip-nx-cache=$(SKIP_NX_CACHE) diff --git a/README.md b/README.md index 7ecfc6356..2dcdc1784 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ The following packages live in `libs/@guardian/*` and are published to NPM: - [@guardian/libs](libs/@guardian/libs) - [@guardian/newsletter-types](libs/@guardian/newsletter-types) - [@guardian/prettier](libs/@guardian/prettier) +- [@guardian/source](libs/@guardian/source) - [@guardian/source-foundations](libs/@guardian/source-foundations) - [@guardian/source-react-components](libs/@guardian/source-react-components) - [@guardian/source-react-components-development-kitchen](libs/@guardian/source-react-components-development-kitchen) @@ -185,6 +186,19 @@ Project-specific tasks are defined as `scripts` in a `package.json` or `targets` - `make @guardian/prettier:fix` - `make @guardian/prettier:lint` +#### @guardian/source + +- `make @guardian/source:build` +- `make @guardian/source:build-storybook` +- `make @guardian/source:build-type-presets` +- `make @guardian/source:create-icons` +- `make @guardian/source:dev` +- `make @guardian/source:fix` +- `make @guardian/source:lint` +- `make @guardian/source:storybook` +- `make @guardian/source:test` +- `make @guardian/source:verify-dist` + #### @guardian/source-foundations - `make @guardian/source-foundations:build` diff --git a/apps/storybooks/.storybook/main.js b/apps/storybooks/.storybook/main.js index d7fb3a2e8..260aed70d 100644 --- a/apps/storybooks/.storybook/main.js +++ b/apps/storybooks/.storybook/main.js @@ -34,6 +34,11 @@ module.exports = { // port set in libs/@guardian/source-react-components-development-kitchen/package.json url: 'http://localhost:4403', }, + source: { + title: 'source', + // port set in libs/@guardian/source/package.json + url: 'http://localhost:4404', + }, }; }, }; diff --git a/configs/rollup.config.js b/configs/rollup.config.js index d234911b7..7e440eddc 100644 --- a/configs/rollup.config.js +++ b/configs/rollup.config.js @@ -1,3 +1,6 @@ +/** @typedef {import("rollup").RollupOptions["plugins"]} Plugins */ +/** @typedef {import("rollup").RollupOptions["input"]} Input */ + import commonjs from '@rollup/plugin-commonjs'; import json from '@rollup/plugin-json'; import { nodeResolve } from '@rollup/plugin-node-resolve'; @@ -5,8 +8,8 @@ import { dts } from 'rollup-plugin-dts'; import esbuild from 'rollup-plugin-esbuild'; import { nodeExternals } from 'rollup-plugin-node-externals'; -/** @type {import("rollup").RollupOptions.input} */ -const input = { index: 'src/index.ts' }; +/** @type {Input} */ +const defaultInput = { index: 'src/index.ts' }; /** @type {import("rollup").RollupOptions.output} */ const output = { @@ -16,23 +19,27 @@ const output = { preserveModulesRoot: 'src', }; -/** @type {import("rollup").RollupOptions.plugins} */ -const plugins = [ +/** @type {Plugins} */ +const defaultPlugins = [ nodeResolve({ - extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'], + extensions: ['.cjs', '.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'], }), commonjs(), json(), - esbuild(), nodeExternals(), ]; -/** @type {import("rollup").RollupOptions} */ -export default [ +/** + * @param {object} param0 + * @param {Plugins} param0.plugins + * @param {Input} param0.input + * @returns {import("rollup").RollupOptions[]} + */ +export default ({ input = defaultInput, plugins = [] }) => [ { input, output, - plugins, + plugins: [...defaultPlugins, ...plugins, esbuild()], }, { input, @@ -41,11 +48,11 @@ export default [ format: 'cjs', entryFileNames: '[name].cjs', }, - plugins, + plugins: [...defaultPlugins, ...plugins, esbuild()], }, { input, output, - plugins: [dts()], + plugins: [...defaultPlugins, ...plugins, dts()], }, ]; diff --git a/libs/@guardian/source/.eslintrc.cjs b/libs/@guardian/source/.eslintrc.cjs new file mode 100644 index 000000000..a74486122 --- /dev/null +++ b/libs/@guardian/source/.eslintrc.cjs @@ -0,0 +1,58 @@ +module.exports = { + extends: ['../../../.eslintrc.cjs'], + ignorePatterns: [ + '!**/*', + 'node_modules', + 'jest.dist.*', // depends on build output, so don't lint it + '.wireit', + 'dist', + 'storybook-static', + ], + overrides: [ + { + files: ['*.ts', '*.tsx'], + parserOptions: { + tsconfigRootDir: __dirname, + }, + rules: {}, + }, + { + files: ['*.js', '*.jsx'], + rules: {}, + }, + { + files: ['**/*.test.*'], + env: { + jest: true, + }, + }, + { + files: ['*.test.ts', '*.stories.*'], + rules: { + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/restrict-template-expressions': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + }, + }, + { + // these are only internal files, so we don't need to check them so + // rigorously they often use things like JSON which are `any`s too, + // we can be more lenient + files: ['scripts/**/*'], + rules: { + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + }, + }, + { + files: ['**/*.stories.tsx'], + rules: { + // storybook require this + 'import/no-default-export': 'off', + }, + }, + ], +}; diff --git a/libs/@guardian/source/.storybook/main.js b/libs/@guardian/source/.storybook/main.js new file mode 100644 index 000000000..2f67c6245 --- /dev/null +++ b/libs/@guardian/source/.storybook/main.js @@ -0,0 +1,22 @@ +const rootMain = require('../../../../.storybook/main'); + +// To customise your Storybook config for this project, update this file + +module.exports = { + ...rootMain, + + core: { ...rootMain.core }, + + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], + addons: [...rootMain.addons], + webpackFinal: async (config, { configType }) => { + // apply any global webpack configs that might have been specified in .storybook/main.js + if (rootMain.webpackFinal) { + config = await rootMain.webpackFinal(config, { configType }); + } + + // add your own webpack tweaks if needed + + return config; + }, +}; diff --git a/libs/@guardian/source/.storybook/manager.ts b/libs/@guardian/source/.storybook/manager.ts new file mode 100644 index 000000000..fcc04c7ae --- /dev/null +++ b/libs/@guardian/source/.storybook/manager.ts @@ -0,0 +1,6 @@ +import { addons } from '@storybook/manager-api'; +import { theme } from './theme'; + +addons.setConfig({ + theme, +}); diff --git a/libs/@guardian/source/.storybook/preview-head.html b/libs/@guardian/source/.storybook/preview-head.html new file mode 100644 index 000000000..e9a2bc0d0 --- /dev/null +++ b/libs/@guardian/source/.storybook/preview-head.html @@ -0,0 +1,386 @@ + diff --git a/libs/@guardian/source/.storybook/preview.tsx b/libs/@guardian/source/.storybook/preview.tsx new file mode 100644 index 000000000..d57fc2bbf --- /dev/null +++ b/libs/@guardian/source/.storybook/preview.tsx @@ -0,0 +1,23 @@ +import { backgrounds } from './preview/backgrounds'; +import { FocusManagerDecorator } from './preview/FocusManagerDecorator'; +import { ThemeProviderDecorator } from './preview/ThemeProviderDecorator'; +import { viewport } from './preview/viewport'; + +export const parameters = { + viewport, + backgrounds, + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, + options: { + storySort: { + method: 'alphabetical', + }, + }, +}; + +export const decorators = [FocusManagerDecorator, ThemeProviderDecorator]; diff --git a/libs/@guardian/source/.storybook/preview/FocusManagerDecorator.tsx b/libs/@guardian/source/.storybook/preview/FocusManagerDecorator.tsx new file mode 100644 index 000000000..bf78b1a2d --- /dev/null +++ b/libs/@guardian/source/.storybook/preview/FocusManagerDecorator.tsx @@ -0,0 +1,11 @@ +import type { Decorator } from '@storybook/react'; +import { useEffect } from 'react'; +import { FocusStyleManager } from '../../src/foundations'; + +export const FocusManagerDecorator: Decorator = (storyFn) => { + useEffect(() => { + FocusStyleManager.onlyShowFocusOnTabs(); + }); + + return <>{storyFn()}; +}; diff --git a/libs/@guardian/source/.storybook/preview/ThemeProviderDecorator.tsx b/libs/@guardian/source/.storybook/preview/ThemeProviderDecorator.tsx new file mode 100644 index 000000000..28b7da8f1 --- /dev/null +++ b/libs/@guardian/source/.storybook/preview/ThemeProviderDecorator.tsx @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment -- storybook type contains `any`s */ + +import { ThemeProvider } from '@emotion/react'; +import type { Decorator } from '@storybook/react'; + +export const ThemeProviderDecorator: Decorator = (storyFn, context) => { + const theme = context.parameters.theme; + return theme ? ( + {storyFn()} + ) : ( + <>{storyFn()} + ); +}; diff --git a/libs/@guardian/source/.storybook/preview/backgrounds.ts b/libs/@guardian/source/.storybook/preview/backgrounds.ts new file mode 100644 index 000000000..2f5f2cf9c --- /dev/null +++ b/libs/@guardian/source/.storybook/preview/backgrounds.ts @@ -0,0 +1,27 @@ +import { palette } from '../../src/foundations'; + +export const backgrounds = { + default: 'background.primary', + values: [ + { + name: 'background.primary', + value: palette.neutral[100], + }, + { + name: 'background.secondary', + value: palette.neutral[97], + }, + { + name: 'background.inverse', + value: palette.neutral[10], + }, + { + name: 'brandBackground.primary', + value: palette.brand[400], + }, + { + name: 'brandAltBackground.primary', + value: palette.brandAlt[400], + }, + ], +}; diff --git a/libs/@guardian/source/.storybook/preview/viewport.ts b/libs/@guardian/source/.storybook/preview/viewport.ts new file mode 100644 index 000000000..17954a4ef --- /dev/null +++ b/libs/@guardian/source/.storybook/preview/viewport.ts @@ -0,0 +1,87 @@ +import type { Breakpoint } from '../../src/foundations'; +import { breakpoints } from '../../src/foundations'; + +type ViewportMeta = { + [key in Breakpoint]: { + name: string; + type: string; + }; +}; +const viewportMeta: ViewportMeta = { + mobile: { + name: 'Mobile', + type: 'mobile', + }, + mobileMedium: { + name: 'Mobile Medium', + type: 'mobile', + }, + mobileLandscape: { + name: 'Mobile Landscape', + type: 'mobile', + }, + phablet: { + name: 'Phablet', + type: 'mobile', + }, + tablet: { + name: 'Tablet', + type: 'tablet', + }, + desktop: { + name: 'Desktop', + type: 'desktop', + }, + leftCol: { + name: 'Left Col', + type: 'desktop', + }, + wide: { + name: 'Wide', + type: 'desktop', + }, +}; + +type Viewports = { + [key in Breakpoint]: { + styles: { + width: string; + height: string; + }; + type: string; + }; +}; + +const viewportEntries = Object.entries(breakpoints).map(([name, width]) => { + return [ + name, + { + name: viewportMeta[name as Breakpoint].name, + styles: { + width: `${width}px`, + height: '100%', + }, + type: viewportMeta[name as Breakpoint].type, + }, + ]; +}); + +const viewportEntriesObject = Object.fromEntries(viewportEntries) as Viewports; + +export const viewport = { + viewports: { + responsive: { + name: 'Responsive', + styles: { + width: '100%', + height: '100%', + border: 'none', + display: 'block', + margin: '0', + boxShadow: 'none', + }, + }, + ...viewportEntriesObject, + }, + defaultViewport: 'responsive', +}; diff --git a/libs/@guardian/source/.storybook/theme.ts b/libs/@guardian/source/.storybook/theme.ts new file mode 100644 index 000000000..505921bd1 --- /dev/null +++ b/libs/@guardian/source/.storybook/theme.ts @@ -0,0 +1,9 @@ +import { create } from '@storybook/theming'; + +export const theme = create({ + base: 'light', + brandTitle: '@guardian/source', + brandUrl: 'https://www.npmjs.com/package/@guardian/source', + brandImage: + 'https://raw.githubusercontent.com/guardian/source/main/assets/logo.png', +}); diff --git a/libs/@guardian/source/README.md b/libs/@guardian/source/README.md new file mode 100644 index 000000000..a21ace586 --- /dev/null +++ b/libs/@guardian/source/README.md @@ -0,0 +1,39 @@ +# `@guardian/source` + + + +> An NPM package containing design foundations and robust, accessible React components from the Guardian's +> [Source Design System](https://theguardian.design). + +[![npm](https://img.shields.io/npm/v/@guardian/source)](https://www.npmjs.com/package/@guardian/source) + + +## Install + +```sh +$ pnpm add @guardian/source +``` + +or + +```sh +$ yarn add @guardian/source +``` + +or + +```sh +$ npm install @guardian/source +``` + +> [!NOTE] +> Use of the React components will require [@emotion/react](https://emotion.sh/docs/introduction#react) + +## Documentation + +Full documentation for components and foundations +is available in the [Source storybook](https://guardian.github.io/storybooks/?path=/docs/source). + +## Contributing + +We welcome contributions to Source! See our [contributing](../../../docs/source/contributing.md) and [Storybook](../../../docs/source/storybook.md) docs for more info. diff --git a/libs/@guardian/source/assets/logo.png b/libs/@guardian/source/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d7e5b6847da687e5630a99f13e131b3c4ea6657b GIT binary patch literal 11116 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 I{DDdM4z>% literal 0 HcmV?d00001 diff --git a/libs/@guardian/source/foundations/package.json b/libs/@guardian/source/foundations/package.json new file mode 100644 index 000000000..7a751b8fa --- /dev/null +++ b/libs/@guardian/source/foundations/package.json @@ -0,0 +1,6 @@ +{ + "//": "sub-package to match the `exports` field in the parent package.json", + "private": true, + "type": "module", + "main": "../dist/foundations.js" +} diff --git a/libs/@guardian/source/jest.config.js b/libs/@guardian/source/jest.config.js new file mode 100644 index 000000000..8217acaad --- /dev/null +++ b/libs/@guardian/source/jest.config.js @@ -0,0 +1,16 @@ +/* eslint-disable import/no-default-export -- that's what jest likes */ + +import { config as baseConfig } from '../../../configs/jest.config.js'; + +/** @typedef {import("jest").Config} Config */ +const config = { + ...baseConfig, + displayName: '@guardian/source', + testEnvironment: 'node', + setupFilesAfterEnv: [ + './lib/jest-matchers/toBeValidCSS.ts', + './lib/jest-matchers/toMatchCSS.ts', + ], +}; + +export default config; diff --git a/libs/@guardian/source/jest.dist.setup.js b/libs/@guardian/source/jest.dist.setup.js new file mode 100644 index 000000000..f613ae0ad --- /dev/null +++ b/libs/@guardian/source/jest.dist.setup.js @@ -0,0 +1,9 @@ +// Mock `src/index` with whatever the dist `package.json` points at. +// This means we can run `src/index.test.ts` against `dist` instead. + +import * as dist from '.'; + +// Register our custom Jest matcher. +import './lib/jest-matchers/toBeValidCSS'; +import './lib/jest-matchers/toMatchCSS'; +jest.mock('./src/index', () => dist); diff --git a/libs/@guardian/source/lib/jest-matchers/@types/index.d.ts b/libs/@guardian/source/lib/jest-matchers/@types/index.d.ts new file mode 100644 index 000000000..3ba9839eb --- /dev/null +++ b/libs/@guardian/source/lib/jest-matchers/@types/index.d.ts @@ -0,0 +1,13 @@ +declare namespace jest { + interface Matchers { + toBeValidCSS(options?: CSSMatcherOptions): R; + toMatchCSS(expected: string, options?: CSSMatcherOptions): R; + } +} + +type CSSMatcherOptions = { + /** + * Set this to true if the CSS is a fragment (e.g. not wrapped in a selector and valid on its own) + */ + isFragment?: boolean; +}; diff --git a/libs/@guardian/source/lib/jest-matchers/toBeValidCSS.ts b/libs/@guardian/source/lib/jest-matchers/toBeValidCSS.ts new file mode 100644 index 000000000..8890c72fa --- /dev/null +++ b/libs/@guardian/source/lib/jest-matchers/toBeValidCSS.ts @@ -0,0 +1,41 @@ +import type { Warning } from 'lightningcss'; +import { transform } from 'lightningcss'; + +expect.extend({ + /** + * Uses the lightningcss library to validate given CSS + * + * @param received - The CSS to validate + * @param options - Specify whether the CSS provided is a fragment (not wrapped in a selector) + */ + toBeValidCSS( + received: string, + options: CSSMatcherOptions = {}, + ): jest.CustomMatcherResult { + const { isFragment = false } = options; + + // We wrap the CSS in a selector if it is a fragment to ensure it is valid. + const finalCSS = isFragment ? `* { ${received} }` : received; + + try { + transform({ + code: Buffer.from(finalCSS, 'utf8'), + filename: '', + }); + + return { + pass: true, + message: () => '', + }; + } catch (error) { + const message = (error as Warning).message; + if (!message) { + throw error; + } + return { + pass: false, + message: () => message, + }; + } + }, +}); diff --git a/libs/@guardian/source/lib/jest-matchers/toMatchCSS.ts b/libs/@guardian/source/lib/jest-matchers/toMatchCSS.ts new file mode 100644 index 000000000..5bb0e6a03 --- /dev/null +++ b/libs/@guardian/source/lib/jest-matchers/toMatchCSS.ts @@ -0,0 +1,59 @@ +import type { Warning } from 'lightningcss'; +import { transform } from 'lightningcss'; + +expect.extend({ + /** + * Uses lightningcss library to normalise and compare given CSS + * + * @param received - The CSS to compare + * @param expected - The expected result + * @param options - Specify whether the CSS being compared is a fragment (not wrapped in a selector) + */ + toMatchCSS( + received: string, + expected: string, + options: CSSMatcherOptions = {}, + ): jest.CustomMatcherResult { + const { isFragment = false } = options; + + // We wrap the CSS in a selector if it is a fragment to ensure it is valid. + const recievedCSS = isFragment ? `* { ${received} }` : received; + const expectedCSS = isFragment ? `* { ${expected} }` : expected; + + try { + const normalisedReceivedCSS = transform({ + code: Buffer.from(recievedCSS, 'utf8'), + filename: '', + }); + + const normalisedExpectedCSS = transform({ + code: Buffer.from(expectedCSS, 'utf8'), + filename: '', + }); + + if ( + normalisedReceivedCSS.code.toString() !== + normalisedExpectedCSS.code.toString() + ) { + throw new Error( + 'Received CSS does not match expected CSS\n\n' + + normalisedReceivedCSS.code.toString(), + ); + } + + return { + pass: true, + message: () => '', + }; + } catch (error) { + const message = (error as Warning).message; + if (!message) { + throw error; + } + return { + pass: false, + message: () => message, + }; + } + }, +}); diff --git a/libs/@guardian/source/package.json b/libs/@guardian/source/package.json new file mode 100644 index 000000000..96a06de69 --- /dev/null +++ b/libs/@guardian/source/package.json @@ -0,0 +1,135 @@ +{ + "name": "@guardian/source", + "version": "0.2.0", + "license": "Apache-2.0", + "sideEffects": false, + "type": "module", + "exports": { + "./foundations": { + "types": "./dist/foundations.d.ts", + "import": "./dist/foundations.js", + "require": "./dist/foundations.cjs" + }, + "./react-components": { + "types": "./dist/react-components.d.ts", + "import": "./dist/react-components.js", + "require": "./dist/react-components.cjs" + } + }, + "files": [ + "dist", + "foundations/package.json", + "react-components/package.json" + ], + "scripts": { + "build": "rm -rf dist && rollup -c", + "build-storybook": "wireit", + "build-type-presets": "tsx ./scripts/build-type-presets.ts", + "create-icons": "tsx scripts/create-icons/index.ts", + "dev": "jest --watch", + "fix": "pnpm lint --fix", + "lint": "eslint --cache .", + "storybook": "storybook dev --port 4404", + "test": "jest --passWithNoTests", + "verify-dist": "jest --setupFilesAfterEnv ./jest.dist.setup.js --passWithNoTests" + }, + "dependencies": { + "mini-svg-data-uri": "1.4.4" + }, + "devDependencies": { + "@babel/core": "7.24.0", + "@emotion/react": "11.11.1", + "@guardian/design-tokens": "workspace:*", + "@rollup/plugin-alias": "5.1.0", + "@svgr/babel-preset": "8.1.0", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "@svgr/plugin-prettier": "8.1.0", + "@svgr/plugin-svgo": "8.1.0", + "@types/jest": "29.5.8", + "@types/mkdirp": "2.0.0", + "@types/prettier": "3.0.0", + "@types/react": "18.2.11", + "dotenv": "16.4.1", + "jest": "29.7.0", + "lightningcss": "1.24.0", + "mkdirp": "3.0.1", + "prettier": "3.2.2", + "react": "18.2.0", + "rollup": "4.17.2", + "ts-jest": "29.1.1", + "tslib": "2.6.2", + "tsx": "4.7.1", + "typescript": "5.3.3", + "wireit": "0.14.4" + }, + "peerDependencies": { + "@emotion/react": "^11.11.1", + "@types/react": "^18.2.11", + "react": "^18.2.0", + "tslib": "^2.6.2", + "typescript": "~5.3.3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "typescript": { + "optional": true + } + }, + "nx": { + "targets": { + "build": { + "inputs": [ + "{projectRoot}/src/**", + "{projectRoot}/package.json", + "{projectRoot}/tsconfig.json" + ], + "outputs": [ + "{projectRoot}/dist" + ] + }, + "lint": { + "inputs": [ + "{projectRoot}/**", + "{workspaceRoot}/.eslint*" + ] + }, + "fix": { + "inputs": [ + "{projectRoot}/**", + "{workspaceRoot}/.eslint*" + ], + "outputs": [ + "{projectRoot}" + ] + }, + "test": { + "inputs": [ + "{projectRoot}/**", + "{workspaceRoot}/jest.*" + ] + }, + "verify-dist": { + "inputs": [ + "{projectRoot}/**", + "{workspaceRoot}/jest.*" + ] + } + } + }, + "wireit": { + "build-storybook": { + "command": "NODE_ENV=production storybook build --webpack-stats-json", + "files": [ + "**/*.{ts,tsx,js,json,mdx,md}", + "!storybook-static/**", + "!jest*" + ], + "output": [ + "storybook-static" + ] + } + } +} diff --git a/libs/@guardian/source/react-components/package.json b/libs/@guardian/source/react-components/package.json new file mode 100644 index 000000000..2d5dfd9b9 --- /dev/null +++ b/libs/@guardian/source/react-components/package.json @@ -0,0 +1,6 @@ +{ + "//": "sub-package to match the `exports` field in the parent package.json", + "private": true, + "type": "module", + "main": "../dist/react-components.js" +} diff --git a/libs/@guardian/source/rollup.config.js b/libs/@guardian/source/rollup.config.js new file mode 100644 index 000000000..04779d3cd --- /dev/null +++ b/libs/@guardian/source/rollup.config.js @@ -0,0 +1,25 @@ +import { resolve } from 'node:path'; +import alias from '@rollup/plugin-alias'; +import config from '../../../configs/rollup.config.js'; + +export default config({ + input: { + foundations: 'src/foundations/index.ts', + 'react-components': 'src/react-components/index.ts', + }, + // only needed while we're using symlinks + plugins: [ + alias({ + entries: [ + { + find: '@guardian/source-foundations', + replacement: resolve('./src/foundations/index.ts'), + }, + { + find: '@guardian/source-react-components', + replacement: resolve('./src/react-components/index.ts'), + }, + ], + }), + ], +}); diff --git a/libs/@guardian/source/scripts/build-type-presets.ts b/libs/@guardian/source/scripts/build-type-presets.ts new file mode 100644 index 000000000..801ee7499 --- /dev/null +++ b/libs/@guardian/source/scripts/build-type-presets.ts @@ -0,0 +1,69 @@ +import fs from 'node:fs'; +import { typography, typographyPresets } from '@guardian/design-tokens'; +import { + fontArrayToString, + pxStringToRem, +} from '../src/foundations/utils/convert-value'; + +const STRIP_WHITESPACE = /^\s+/gm; +const STRIP_TABS = /^\t{3}|\t{2}/gm; + +const { fontSize, textDecorationThicknessForFontSize } = typography; + +type FontSize = keyof typeof fontSize; + +const textDecorationThickness = (size: string) => + textDecorationThicknessForFontSize[size.slice(0, -2) as FontSize]; + +console.log('Building typography presets…'); + +const presetTotal = Object.keys(typographyPresets).length; + +const outputPath = `${process.cwd()}/src/foundations/__generated__/typography`; +fs.mkdirSync(outputPath, { recursive: true }); + +const cssOutputPath = `${outputPath}/css.ts`; +const objectOutputPath = `${outputPath}/objects.ts`; + +const banner = ` + // Typography presets + // Auto-generated by scripts/build-type-presets.ts + // DO NOT EDIT +`.replace(STRIP_WHITESPACE, ''); + +// Generate CSS representation of presets as a string +const css = Object.entries(typographyPresets) + .map( + ([preset, properties]) => ` + export const ${preset} = \` + font-family: ${fontArrayToString(properties.fontFamily)}; + font-size: ${pxStringToRem(properties.fontSize)}rem; + line-height: ${properties.lineHeight}; + font-weight: ${properties.fontWeight}; + font-style: ${properties.fontStyle}; + --source-text-decoration-thickness: ${textDecorationThickness(properties.fontSize)}; + \`; + `, + ) + .join('') + .replace(STRIP_TABS, ''); + +// Generate object literal representation of presets +const object = Object.entries(typographyPresets) + .map( + ([preset, properties]) => ` + export const ${preset}Object = { + fontFamily: '${fontArrayToString(properties.fontFamily)}', + fontSize: '${pxStringToRem(properties.fontSize)}rem', + lineHeight: ${properties.lineHeight}, + fontWeight: ${properties.fontWeight}, + fontStyle: '${properties.fontStyle}', + } as const; + `, + ) + .join('') + .replace(STRIP_TABS, ''); + +fs.writeFileSync(cssOutputPath, banner + css); +fs.writeFileSync(objectOutputPath, banner + object); +console.log(`✓ ${presetTotal} presets built`); diff --git a/libs/@guardian/source/scripts/create-icons/README.md b/libs/@guardian/source/scripts/create-icons/README.md new file mode 100644 index 000000000..61e3c854f --- /dev/null +++ b/libs/@guardian/source/scripts/create-icons/README.md @@ -0,0 +1,34 @@ +# `@guardian/source-react-components` scripts + +## `create-icons` + +Fetches the SVG data for the Source icon set from Figma and generates React components using [SVGR](https://github.com/gregberge/svgr). + +The script can be run with: + +```sh +pnpm --filter @guardian/source create-icons +``` + +### Personal access token + +In order to run the script you will need a personal access token to authenticate with the Figma API. + +1. Log in to your Figma account. +2. Select **Help and account > Account settings** in Figma's main menu. +3. Go to the **Personal access tokens** section under the **Account** tab and add a description to generate a new token. +4. A new token will be generated immediately. _Note:_ this is your **only** chance to copy the token. + +The token is passed to the script via an environment variable called `FIGMA_TOKEN`. This can be set on the command line when running the script: + +```sh +FIGMA_TOKEN=TOKEN_GOES_HERE pnpm --filter @guardian/source create-icons +``` + +Or you can create a `.env` file in the root of the `source-react-components` package and set the variable there to avoid having to provide it every time you run the script: + +```env +FIGMA_TOKEN=TOKEN_GOES_HERE +``` + +_Note:_ The script does not run as part of the build process to ensure we don't inadvertently pick up changes to icons as part of an unrelated change. diff --git a/libs/@guardian/source/scripts/create-icons/create-icon-component.ts b/libs/@guardian/source/scripts/create-icons/create-icon-component.ts new file mode 100644 index 000000000..6a655eea2 --- /dev/null +++ b/libs/@guardian/source/scripts/create-icons/create-icon-component.ts @@ -0,0 +1,192 @@ +import type { TransformOptions } from '@babel/core'; +import { transform } from '@svgr/core'; +import type { Config } from '@svgr/core'; +import { labels } from '../../src/react-components/icons/labels'; + +// SVGR uses babel under the hood to create the jsx +// this is our custom transform for it to use +const createBabelConfig = ({ retainFill }: { retainFill: boolean }) => { + const babelConfig: TransformOptions = {}; + const plugins = []; + + // remove fill attribute from all icons except those with special fill colours + if (!retainFill) { + plugins.push([ + '@svgr/babel-plugin-remove-jsx-attribute', + { + elements: ['svg', 'path'], + attributes: ['fill'], + }, + ]); + plugins.push([ + '@svgr/babel-plugin-add-jsx-attribute', + { + elements: ['path'], + attributes: [ + { + name: 'fill', + value: 'theme?.fill', + spread: false, + literal: true, + position: 'end', + }, + ], + }, + ]); + } + + // replace viewbox with legacy 30x30 viewbox + // TODO: a future version of Source should expose icons with the viewboxes + // defined in Figma + plugins.push([ + '@svgr/babel-plugin-replace-jsx-attribute-value', + { + values: [ + { value: '0 0 24 24', newValue: '-3 -3 30 30' }, + // legacy 50x20 wide viewbox + { value: '0 0 48 24', newValue: '-1 2 50 20' }, + ], + }, + ]); + + if (plugins.length === 0) { + return {}; + } + + babelConfig.plugins = plugins; + + return babelConfig; +}; + +type CreateIconComponentProps = { + icon: { name: string; svg: string }; + retainFill: boolean; + isWideIcon: boolean; +}; +export const createIconComponent = async ({ + icon, + retainFill, + isWideIcon, +}: CreateIconComponentProps) => { + // SVGR template + // https://react-svgr.com/docs/node-api/ + const template: Config['template'] = (variables, { tpl }) => { + if (!retainFill) { + return tpl` + import { css } from '@emotion/react'; + + import { iconSize, visuallyHidden } from '../../../foundations'; + import type { IconProps } from '../..'; + + ${variables.imports}; + + const ${variables.componentName} = ({ + size, + theme, + }: IconProps) => ( + ${variables.jsx} + ); + `; + } + return tpl` + import { css } from '@emotion/react'; + + import { iconSize, visuallyHidden } from '../../../foundations'; + import type { IconProps } from '../..'; + + ${variables.imports}; + + const ${variables.componentName} = ({ + size, + }: IconProps) => ( + ${variables.jsx} + ); + `; + }; + + const svgProps = { + focusable: '{false}', + 'aria-hidden': '{true}', + width: isWideIcon ? '{undefined}' : '{size ? iconSize[size] : undefined}', + height: isWideIcon ? '{size ? iconSize[size] : undefined}' : '{undefined}', + }; + + const svgComponentName = 'Svg'; + + const iconComponentName = + 'Svg' + + icon.name + .split('-') + .map((s) => { + const [firstLetter, ...rest] = s; + if (firstLetter) { + return firstLetter.toLocaleUpperCase() + rest.join(''); + } + return s; + }) + .join(''); + + const iconComponent = await transform( + icon.svg, + { + icon: true, + plugins: [ + '@svgr/plugin-svgo', + '@svgr/plugin-jsx', + '@svgr/plugin-prettier', + ], + jsx: { + babelConfig: createBabelConfig({ retainFill }), + }, + svgoConfig: { + plugins: [ + { name: 'cleanupIds', params: { minify: true } }, + { name: 'prefixIds', params: { prefix: icon.name } }, + { name: 'convertPathData' }, + ], + }, + typescript: true, + jsxRuntime: 'automatic', + expandProps: false, + svgProps, + template, + }, + { + componentName: svgComponentName, + }, + ); + + const label = labels[icon.name]; + + if (!label) { + // This error is thrown when the accessible label data for the icon is missing in Figma. + throw new Error( + `Warning: No accessible label found for ${icon.name}! Please double check that it is specified correctly in Figma.`, + ); + } + + const iconComponentExport = ` + export const ${iconComponentName} = ({ + size,${!retainFill ? '\ntheme,' : ''} + isAnnouncedByScreenReader = false, + }: IconProps) => ( + <> + <${svgComponentName} size={size} ${!retainFill ? 'theme={theme}' : ''} /> + {isAnnouncedByScreenReader ? ( + + ${label} + + ) : ( + '' + )} + + );`; + return { + componentName: iconComponentName, + component: [iconComponent, iconComponentExport].join('\n'), + }; +}; diff --git a/libs/@guardian/source/scripts/create-icons/create-readme.ts b/libs/@guardian/source/scripts/create-icons/create-readme.ts new file mode 100644 index 000000000..c119c8dec --- /dev/null +++ b/libs/@guardian/source/scripts/create-icons/create-readme.ts @@ -0,0 +1,15 @@ +import { ICON_FILE } from './get-svgs-from-figma'; + +export const createReadme = () => `# Icons + +**The contents of this directory are created automatically. Any edits will be +overwritten sooner or later.** + +The SVGs for these icons are automatically pulled in from the [source design file in +Figma](https://www.figma.com/file/${ICON_FILE}/%E2%97%90-Icons?node-id=55%3A2) +using the create-icons script via the Figma API: + +\`\`\`sh +yarn workspace @guardian/source-react-components create-icons +\`\`\` +`; diff --git a/libs/@guardian/source/scripts/create-icons/get-svgs-from-figma.ts b/libs/@guardian/source/scripts/create-icons/get-svgs-from-figma.ts new file mode 100644 index 000000000..0ce266004 --- /dev/null +++ b/libs/@guardian/source/scripts/create-icons/get-svgs-from-figma.ts @@ -0,0 +1,88 @@ +import 'dotenv/config'; + +interface FigmaComponentsResponse { + meta: { + components: Array<{ + containing_frame?: { + name: string; + }; + name: string; + node_id: string; + description: string; + }>; + }; +} +interface FigmaImagesResponse { + images: Record; +} + +export const ICON_FILE = 'b2qv2OMLoNCYnP01ipfrP7'; + +if (!process.env.FIGMA_TOKEN) { + console.log('FIGMA_TOKEN not set. Please add it to your .env file.'); + console.log('See https://www.figma.com/developers/api#access-tokens'); + process.exit(1); +} + +const FIGMA_API_OPTIONS = { + headers: { + 'X-Figma-Token': process.env.FIGMA_TOKEN, + }, +}; + +const figmaApi = async (url: string): Promise => { + const response = await fetch( + `https://api.figma.com/v1/${url}`, + FIGMA_API_OPTIONS, + ); + return response.json() as Promise; +}; + +export const getIconsFromFigma = async () => { + // Get a list of available (figma) components from Figma + // https://www.figma.com/developers/api#library-items-types + const figmaComponents = ( + await figmaApi(`files/${ICON_FILE}/components`) + ).meta.components; + + // filter out the icons from the list of figma components + const figmaIconComponents = figmaComponents.filter((c) => { + return ( + // Only get icons that are in a certain frame + c.containing_frame && c.containing_frame.name === 'UI icons 24x24' + ); + }); + + const iconIds = figmaIconComponents.map(({ node_id }) => node_id).join(','); + + // Get the URLs we can fetch actual images from from Figma + // https://www.figma.com/developers/api#get-images-endpoint + const figmaIconSvgUrlsByNodeId = ( + await figmaApi( + `images/${ICON_FILE}/?ids=${iconIds}&format=svg`, + ) + ).images; + + const icons = []; + + for (const icon of figmaIconComponents) { + const url = figmaIconSvgUrlsByNodeId[icon.node_id]; + + if (url) { + console.log(`Fetching ${icon.name}.svg`); + + // Fetch SVG markup from Figma + const response = await fetch(url); + const svg = await response.text(); + + icons.push({ + name: icon.name, + svg, + }); + } else { + throw new Error('No URL found for icon'); + } + } + + return icons; +}; diff --git a/libs/@guardian/source/scripts/create-icons/index.ts b/libs/@guardian/source/scripts/create-icons/index.ts new file mode 100644 index 000000000..15c525c7a --- /dev/null +++ b/libs/@guardian/source/scripts/create-icons/index.ts @@ -0,0 +1,89 @@ +import { promises } from 'fs'; +import path from 'path'; +import prettierConfig from '@guardian/prettier'; +import { mkdirp } from 'mkdirp'; +import { format } from 'prettier'; +import { createIconComponent } from './create-icon-component'; +import { createReadme } from './create-readme'; +import { getIconsFromFigma } from './get-svgs-from-figma'; + +const { writeFile, rm } = promises; + +// most icons are the same and can be run through the component generator +// but some are different and need to be handled separately +const SPECIAL_CASES = { + retainFill: [ + 'apple-brand', + 'facebook-brand', + 'google-brand', + 'pay-pal-brand', + 'telegram-brand', + 'signal-brand', + 'whats-app-brand', + ], + isWide: ['direct-debit-wide'], +}; + +const VENDOR_ICON_PATH = path.resolve( + import.meta.dirname, + '..', + '..', + 'src', + 'react-components', + '__generated__', + 'icons', +); + +const warning = [ + '// DO NOT EDIT', + '// this file is auto-generated by packages/@guardian/source/scripts/create-icons/index.ts', + '', +].join('\n'); + +// now we start fetching SVGs and building our components +void (async () => { + console.log('Removing old icons'); + + // remove any existing icons from a previous run + try { + await rm(VENDOR_ICON_PATH, { recursive: true }); + } catch (e) { + // do nothing + } + + // create the icons directory + await mkdirp(VENDOR_ICON_PATH); + + console.log('Creating readme'); + + // create a readme + await writeFile( + path.resolve(VENDOR_ICON_PATH, 'README.md'), + createReadme(), + 'utf8', + ); + + console.log('Getting SVGs from Figma'); + + const icons = await getIconsFromFigma(); + + console.log(`Creating components`); + + // fetch the SVGs from Figma, create a react component from them using svgr + // and save them both + for (const icon of icons) { + const { component, componentName } = await createIconComponent({ + icon, + retainFill: SPECIAL_CASES.retainFill.includes(icon.name), + isWideIcon: SPECIAL_CASES.isWide.includes(icon.name), + }); + + const filepath = path.resolve(VENDOR_ICON_PATH, `${componentName}.tsx`); + + await writeFile( + filepath, + await format(warning + component, { filepath, ...prettierConfig }), + 'utf8', + ); + } +})(); diff --git a/libs/@guardian/source/src/foundations b/libs/@guardian/source/src/foundations new file mode 120000 index 000000000..b330ebf95 --- /dev/null +++ b/libs/@guardian/source/src/foundations @@ -0,0 +1 @@ +../../source-foundations/src/ \ No newline at end of file diff --git a/libs/@guardian/source/src/react-components b/libs/@guardian/source/src/react-components new file mode 120000 index 000000000..936be046c --- /dev/null +++ b/libs/@guardian/source/src/react-components @@ -0,0 +1 @@ +../../source-react-components/src/ \ No newline at end of file diff --git a/libs/@guardian/source/tsconfig.json b/libs/@guardian/source/tsconfig.json new file mode 100644 index 000000000..c957390f2 --- /dev/null +++ b/libs/@guardian/source/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "include": [".", ".storybook/*"], + "compilerOptions": { + "paths": { + "@guardian/source-foundations": [ + "libs/@guardian/source/src/foundations/index.ts" + ], + "@guardian/source-react-components": [ + "libs/@guardian/source/src/react-components/index.ts" + ] + } + }, + "references": [ + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/@guardian/source/tsconfig.spec.json b/libs/@guardian/source/tsconfig.spec.json new file mode 100644 index 000000000..914db19cb --- /dev/null +++ b/libs/@guardian/source/tsconfig.spec.json @@ -0,0 +1,21 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "allowJs": true, + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.js", + "**/*.test.ts", + "**/*.spec.ts", + "**/*.test.tsx", + "**/*.spec.tsx", + "**/*.test.js", + "**/*.spec.js", + "**/*.test.jsx", + "**/*.spec.jsx", + "**/*.d.ts" + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ef467f597..935c051d8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -136,7 +136,7 @@ importers: version: 9.0.6 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.12.11) + version: 29.7.0(@types/node@20.12.11)(ts-node@10.9.2) jest-environment-jsdom: specifier: 29.7.0 version: 29.7.0 @@ -483,7 +483,7 @@ importers: version: 16.0.0(tslib@2.6.2)(typescript@5.3.3) jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.12.11) + version: 29.7.0(@types/node@20.12.11)(ts-node@10.9.2) jest-environment-jsdom: specifier: 29.7.0 version: 29.7.0 @@ -581,6 +581,88 @@ importers: specifier: 2.6.2 version: 2.6.2 + libs/@guardian/source: + dependencies: + mini-svg-data-uri: + specifier: 1.4.4 + version: 1.4.4 + devDependencies: + '@babel/core': + specifier: 7.24.0 + version: 7.24.0 + '@emotion/react': + specifier: 11.11.1 + version: 11.11.1(@types/react@18.2.11)(react@18.2.0) + '@guardian/design-tokens': + specifier: workspace:* + version: link:../design-tokens + '@rollup/plugin-alias': + specifier: 5.1.0 + version: 5.1.0(rollup@4.17.2) + '@svgr/babel-preset': + specifier: 8.1.0 + version: 8.1.0(@babel/core@7.24.0) + '@svgr/core': + specifier: 8.1.0 + version: 8.1.0(typescript@5.3.3) + '@svgr/plugin-jsx': + specifier: 8.1.0 + version: 8.1.0(@svgr/core@8.1.0) + '@svgr/plugin-prettier': + specifier: 8.1.0 + version: 8.1.0(@svgr/core@8.1.0) + '@svgr/plugin-svgo': + specifier: 8.1.0 + version: 8.1.0(@svgr/core@8.1.0)(typescript@5.3.3) + '@types/jest': + specifier: 29.5.8 + version: 29.5.8 + '@types/mkdirp': + specifier: 2.0.0 + version: 2.0.0 + '@types/prettier': + specifier: 3.0.0 + version: 3.0.0 + '@types/react': + specifier: 18.2.11 + version: 18.2.11 + dotenv: + specifier: 16.4.1 + version: 16.4.1 + jest: + specifier: 29.7.0 + version: 29.7.0(@types/node@20.12.11)(ts-node@10.9.2) + lightningcss: + specifier: 1.24.0 + version: 1.24.0 + mkdirp: + specifier: 3.0.1 + version: 3.0.1 + prettier: + specifier: 3.2.2 + version: 3.2.2 + react: + specifier: 18.2.0 + version: 18.2.0 + rollup: + specifier: 4.17.2 + version: 4.17.2 + ts-jest: + specifier: 29.1.1 + version: 29.1.1(@babel/core@7.24.0)(esbuild@0.20.2)(jest@29.7.0)(typescript@5.3.3) + tslib: + specifier: 2.6.2 + version: 2.6.2 + tsx: + specifier: 4.7.1 + version: 4.7.1 + typescript: + specifier: 5.3.3 + version: 5.3.3 + wireit: + specifier: 0.14.4 + version: 0.14.4 + libs/@guardian/source-foundations: dependencies: mini-svg-data-uri: @@ -912,14 +994,14 @@ packages: dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.4 + '@babel/generator': 7.24.5 '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) - '@babel/helpers': 7.24.4 - '@babel/parser': 7.24.4 + '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.0) + '@babel/helpers': 7.24.5 + '@babel/parser': 7.24.5 '@babel/template': 7.24.0 - '@babel/traverse': 7.24.1 - '@babel/types': 7.24.0 + '@babel/traverse': 7.24.5 + '@babel/types': 7.24.5 convert-source-map: 2.0.0 debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 @@ -932,35 +1014,12 @@ packages: /@babel/core@7.24.4: resolution: {integrity: sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==} engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.4 - '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.4) - '@babel/helpers': 7.24.4 - '@babel/parser': 7.24.4 - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.1 - '@babel/types': 7.24.0 - convert-source-map: 2.0.0 - debug: 4.3.4(supports-color@8.1.1) - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/core@7.24.5: - resolution: {integrity: sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==} - engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.24.2 '@babel/generator': 7.24.5 '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5) + '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.4) '@babel/helpers': 7.24.5 '@babel/parser': 7.24.5 '@babel/template': 7.24.0 @@ -979,7 +1038,7 @@ packages: resolution: {integrity: sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 @@ -1182,20 +1241,6 @@ packages: '@babel/helper-validator-identifier': 7.22.20 dev: true - /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.4): - resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.24.4 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.24.3 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.24.5 - '@babel/helper-validator-identifier': 7.22.20 - dev: true - /@babel/helper-module-transforms@7.24.5(@babel/core@7.24.0): resolution: {integrity: sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==} engines: {node: '>=6.9.0'} @@ -1210,13 +1255,13 @@ packages: '@babel/helper-validator-identifier': 7.24.5 dev: true - /@babel/helper-module-transforms@7.24.5(@babel/core@7.24.5): + /@babel/helper-module-transforms@7.24.5(@babel/core@7.24.4): resolution: {integrity: sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.5 + '@babel/core': 7.24.4 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-module-imports': 7.24.3 '@babel/helper-simple-access': 7.24.5 @@ -1346,17 +1391,6 @@ packages: '@babel/types': 7.24.5 dev: true - /@babel/helpers@7.24.4: - resolution: {integrity: sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.1 - '@babel/types': 7.24.0 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/helpers@7.24.5: resolution: {integrity: sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==} engines: {node: '>=6.9.0'} @@ -1399,7 +1433,7 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true /@babel/parser@7.24.5: @@ -1473,15 +1507,6 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.5): - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.0 - dev: true - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: @@ -1491,15 +1516,6 @@ packages: '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.5): - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.5 - dev: true - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.0): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: @@ -1509,15 +1525,6 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.5): - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.0 - dev: true - /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.0): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} @@ -1595,15 +1602,6 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.5): - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.0 - dev: true - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: @@ -1613,15 +1611,6 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.5): - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.0 - dev: true - /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} engines: {node: '>=6.9.0'} @@ -1661,15 +1650,6 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.5): - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.0 - dev: true - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: @@ -1679,15 +1659,6 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.5): - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.0 - dev: true - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.0): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: @@ -1697,15 +1668,6 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.5): - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.0 - dev: true - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: @@ -1715,15 +1677,6 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.5): - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.0 - dev: true - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: @@ -1733,15 +1686,6 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.5): - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.0 - dev: true - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.0): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: @@ -1751,15 +1695,6 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.5): - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.0 - dev: true - /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.0): resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} @@ -1780,16 +1715,6 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.5): - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-plugin-utils': 7.24.0 - dev: true - /@babel/plugin-syntax-typescript@7.24.1(@babel/core@7.24.0): resolution: {integrity: sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==} engines: {node: '>=6.9.0'} @@ -2293,7 +2218,7 @@ packages: '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.24.5 '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.0) - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true /@babel/plugin-transform-react-jsx@7.23.4(@babel/core@7.24.4): @@ -2307,7 +2232,7 @@ packages: '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.24.5 '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.4) - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true /@babel/plugin-transform-react-pure-annotations@7.24.1(@babel/core@7.24.0): @@ -2669,12 +2594,12 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.4 + '@babel/generator': 7.24.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 '@babel/helper-hoist-variables': 7.22.5 '@babel/helper-split-export-declaration': 7.24.5 - '@babel/parser': 7.24.4 + '@babel/parser': 7.24.5 '@babel/types': 7.24.5 debug: 4.3.4(supports-color@8.1.1) globals: 11.12.0 @@ -3090,7 +3015,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.0 + '@babel/runtime': 7.24.5 '@emotion/babel-plugin': 11.11.0 '@emotion/cache': 11.11.0 '@emotion/serialize': 1.1.2 @@ -3754,49 +3679,6 @@ packages: slash: 3.0.0 dev: true - /@jest/core@29.7.0: - resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.12.11 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.12.11) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.5 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - dev: true - /@jest/core@29.7.0(ts-node@10.9.2): resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4053,7 +3935,7 @@ packages: /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: - '@babel/runtime': 7.24.0 + '@babel/runtime': 7.24.5 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 @@ -4293,7 +4175,7 @@ packages: '@phenomnomnominal/tsquery': 5.0.1(typescript@5.3.3) chalk: 4.1.2 identity-obj-proxy: 3.0.0 - jest-config: 29.7.0(@types/node@20.12.11) + jest-config: 29.7.0(@types/node@20.12.11)(ts-node@10.9.2) jest-resolve: 29.7.0 jest-util: 29.7.0 minimatch: 9.0.3 @@ -4517,6 +4399,19 @@ packages: react: 18.2.0 dev: true + /@rollup/plugin-alias@5.1.0(rollup@4.17.2): + resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + rollup: 4.17.2 + slash: 4.0.0 + dev: true + /@rollup/plugin-commonjs@25.0.7(rollup@4.17.2): resolution: {integrity: sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==} engines: {node: '>=14.0.0'} @@ -5055,7 +4950,7 @@ packages: dependencies: '@babel/core': 7.24.0 '@babel/preset-env': 7.24.0(@babel/core@7.24.0) - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 '@storybook/csf': 0.1.2 '@storybook/csf-tools': 8.0.5 '@storybook/node-logger': 8.0.5 @@ -5215,10 +5110,10 @@ packages: /@storybook/csf-tools@8.0.5: resolution: {integrity: sha512-fW2hAO57ayq7eHjpS5jXy/AKm3oZxApngd9QU/bC800EyTWENwLPxFnHLAE86N57Dc3bcE4PTFCyqpxzE4Uc7g==} dependencies: - '@babel/generator': 7.24.4 - '@babel/parser': 7.24.4 - '@babel/traverse': 7.24.1 - '@babel/types': 7.24.0 + '@babel/generator': 7.24.5 + '@babel/parser': 7.24.5 + '@babel/traverse': 7.24.5 + '@babel/types': 7.24.5 '@storybook/csf': 0.1.2 '@storybook/types': 8.0.5 fs-extra: 11.2.0 @@ -5657,7 +5552,7 @@ packages: resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} engines: {node: '>=14'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 entities: 4.5.0 dev: true @@ -7489,24 +7384,6 @@ packages: - supports-color dev: true - /babel-jest@29.7.0(@babel/core@7.24.5): - resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - dependencies: - '@babel/core': 7.24.5 - '@jest/transform': 29.7.0 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.24.5) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: true - /babel-loader@9.1.3(@babel/core@7.24.0)(webpack@5.91.0): resolution: {integrity: sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==} engines: {node: '>= 14.15.0'} @@ -7678,26 +7555,6 @@ packages: '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.0) dev: true - /babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.5): - resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.24.5 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.5) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.5) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.5) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.5) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.5) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.5) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.5) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.5) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.5) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.5) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.5) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.5) - dev: true - /babel-preset-jest@29.6.3(@babel/core@7.24.0): resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -7709,17 +7566,6 @@ packages: babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.0) dev: true - /babel-preset-jest@29.6.3(@babel/core@7.24.5): - resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.24.5 - babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.5) - dev: true - /bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} dev: true @@ -8510,25 +8356,6 @@ packages: typescript: 5.3.3 dev: true - /create-jest@29.7.0(@types/node@20.12.11): - resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.12.11) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - /create-jest@29.7.0(@types/node@20.12.11)(ts-node@10.9.2): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -11665,34 +11492,6 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@20.12.11): - resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.12.11) - exit: 0.1.2 - import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.12.11) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - /jest-cli@29.7.0(@types/node@20.12.11)(ts-node@10.9.2): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -11721,7 +11520,7 @@ packages: - ts-node dev: true - /jest-config@29.7.0(@types/node@20.12.11): + /jest-config@29.7.0(@types/node@20.12.11)(ts-node@10.9.2): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -11756,46 +11555,6 @@ packages: pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - - /jest-config@29.7.0(@types/node@20.12.11)(ts-node@10.9.2): - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - dependencies: - '@babel/core': 7.24.5 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.12.11 - babel-jest: 29.7.0(@babel/core@7.24.5) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 ts-node: 10.9.2(@swc/core@1.4.0)(@types/node@20.12.11)(typescript@5.3.3) transitivePeerDependencies: - babel-plugin-macros @@ -12047,7 +11806,7 @@ packages: dependencies: '@babel/core': 7.24.0 '@babel/generator': 7.24.5 - '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.0) '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.0) '@babel/types': 7.24.5 '@jest/expect-utils': 29.7.0 @@ -12126,27 +11885,6 @@ packages: supports-color: 8.1.1 dev: true - /jest@29.7.0(@types/node@20.12.11): - resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.7.0 - '@jest/types': 29.6.3 - import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.12.11) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - /jest@29.7.0(@types/node@20.12.11)(ts-node@10.9.2): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -16081,7 +15819,7 @@ packages: bs-logger: 0.2.6 esbuild: 0.20.2 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.12.11) + jest: 29.7.0(@types/node@20.12.11)(ts-node@10.9.2) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7a359a0a0..9de2cd016 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,6 @@ packages: - 'apps/**' - 'libs/**' + - '!**/libs/@guardian/source/*/**' - 'tools/**' - 'configs'