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 000000000..d7e5b6847
Binary files /dev/null and b/libs/@guardian/source/assets/logo.png differ
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'