diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d6fc35f1ba..4983e0bf3e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -54,7 +54,7 @@ jobs: pnpm install --frozen-lockfile # See the same step's comment in the `test` job above. pnpm --filter carbon-components-ember exec playwright install chromium - pnpm --filter carbon-components-ember create-autogenerated-files + pnpm --filter carbon-components-ember generate - name: Test stories run: pnpm --filter carbon-components-ember test:storybook --shard=${{ matrix.shard }}/${{ strategy.job-total }} @@ -73,7 +73,7 @@ jobs: - name: Install dependencies run: | pnpm install --frozen-lockfile - pnpm --filter carbon-components-ember create-autogenerated-files + pnpm --filter carbon-components-ember generate - name: Build Storybook run: pnpm build:storybook @@ -91,7 +91,7 @@ jobs: # Packing the addon writes its agent skill's references from this # build; fail here, not at release, if the manifest stops fitting. - name: Write the skill's component references - run: pnpm --filter carbon-components-ember create-skill-references + run: pnpm --filter carbon-components-ember generate:skill-references # The deploy below publishes this build rather than building again. - name: Upload the site diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index d13a124253..97ba7a8944 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -40,7 +40,7 @@ jobs: - name: Install dependencies run: | pnpm install --frozen-lockfile - pnpm --filter carbon-components-ember create-autogenerated-files + pnpm --filter carbon-components-ember generate - name: Lint env: @@ -110,7 +110,7 @@ jobs: pnpm install --frozen-lockfile # pnpm's side-effects cache can skip playwright-chromium's install script on a store cache hit, even though the browsers it downloads live outside the store and don't persist across runners. pnpm --filter carbon-components-ember exec playwright install chromium - pnpm --filter carbon-components-ember create-autogenerated-files + pnpm --filter carbon-components-ember generate - name: Run tests run: pnpm run test @@ -150,7 +150,7 @@ jobs: pnpm install --no-frozen-lockfile # See the same step's comment in the `test` job above. pnpm --filter carbon-components-ember exec playwright install chromium - pnpm --filter carbon-components-ember create-autogenerated-files + pnpm --filter carbon-components-ember generate - name: Run tests run: pnpm test diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml index d6f1f492eb..93e027be8c 100644 --- a/.github/workflows/pr-preview.yml +++ b/.github/workflows/pr-preview.yml @@ -23,7 +23,7 @@ jobs: - name: Install dependencies run: | pnpm install --frozen-lockfile - pnpm --filter carbon-components-ember create-autogenerated-files + pnpm --filter carbon-components-ember generate - name: Build Storybook run: pnpm build:storybook diff --git a/.github/workflows/templates/agent-prompt.md b/.github/workflows/templates/agent-prompt.md index 981170922e..91864ce507 100644 --- a/.github/workflows/templates/agent-prompt.md +++ b/.github/workflows/templates/agent-prompt.md @@ -143,7 +143,7 @@ component synced and push that update (see Phase 4, step 3). - If a prop/story implies an icon-rendering slot, use the icon components from `carbon-components-ember/icons` (see AGENTS.md pitfall on icon registration below — the same registration step applies here) 3. **Export Component** - - Run `pnpm --filter carbon-components-ember create-autogenerated-files`; + - Run `pnpm --filter carbon-components-ember generate`; it adds the component to the generated barrel, named after its file 4. **Create Tests** @@ -195,7 +195,7 @@ Use this path only when the component genuinely doesn't belong as a standalone E `{{COMPONENT_NAME_KEBAB}}/item.gts`. The barrel names each export after its file, and this fails if two files would export the same name: ```bash - pnpm --filter carbon-components-ember create-autogenerated-files + pnpm --filter carbon-components-ember generate ``` ### Phase 4: Documentation (Required) diff --git a/AGENTS.md b/AGENTS.md index 6864d5637f..37a2185693 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -186,7 +186,7 @@ Signatures follow the [Ember guides](https://guides.emberjs.com/release/typescri Template keywords such as `fn`, `on`, `eq`, and `element` resolve to a same-named JavaScript binding in scope. Alias such imports when using the template keyword: import Storybook's spy as `fn as spy`. Never import `fn` or `on` from Ember (`ember/no-legacy-helper-imports`). ### ❌ Pitfall 2: Naming a File Differently from Its Export -File names are kebab-case, and `pnpm create-autogenerated-files` names each export of the `carbon-components-ember/components`, `/ai-chat` and `/charts` barrels after its file: `text-input.gts` exports `TextInput`, and a top-level `ai-` or `ui-` file spells the acronym (`AILabel`, `UIShell`). It fails when two files would export the same name from one barrel. So name a file after its export in full: `toggletip/toggletip-button.gts`, not `toggletip/button.gts`. Two files sharing a basename (`tile/group.gts` and `radio-button/group.gts`) once made the production rollup build mis-name exports. +File names are kebab-case, and `pnpm generate` names each export of the `carbon-components-ember/components`, `/ai-chat` and `/charts` barrels after its file: `text-input.gts` exports `TextInput`, and a top-level `ai-` or `ui-` file spells the acronym (`AILabel`, `UIShell`). It fails when two files would export the same name from one barrel. So name a file after its export in full: `toggletip/toggletip-button.gts`, not `toggletip/button.gts`. Two files sharing a basename (`tile/group.gts` and `radio-button/group.gts`) once made the production rollup build mis-name exports. ### ❌ Pitfall 3: MutationObservers to inspect Child DOM Nodes Fights the Glimmer render pipeline. Instead, use contextual components so children render with correct classes/attributes declaratively. @@ -286,7 +286,7 @@ AI Chat's components live in `src/components/ai-chat/` and are exported from the - [ ] Create `.gts` file in `carbon-components-ember/src/components/` - [ ] Define TypeScript signature — fully typed, no `any` - [ ] Use `cds--` prefix for CSS classes -- [ ] Export it: run `pnpm create-autogenerated-files` to regenerate public barrel exports +- [ ] Export it: run `pnpm generate` to regenerate public barrel exports - [ ] Match React's props: `pnpm --filter dom-parity args --component ` - [ ] When the PR closes a `[Parity Check]` issue, run `node scripts/parity-check.mjs --mark-synced ` (with `GITHUB_TOKEN`) and commit `.parity-check-data.json`, or the weekly check reopens it; a React directory with no same-named export needs `--exclude --reason "..."` - [ ] Create test file in `tests/components/` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c2177f64b9..1757af042c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ pnpm switches to it automatically (or run `corepack enable` once). Node is pinned in `package.json#devEngines.runtime`: pnpm downloads that version and runs every script with it, whatever Node you have installed. -- `pnpm --filter carbon-components-ember create-autogenerated-files` – generates the icon components and index files +- `pnpm --filter carbon-components-ember generate` – generates the icon components and the barrels ## Linting diff --git a/carbon-components-ember/.gitignore b/carbon-components-ember/.gitignore index 12c90ef257..31ec931ed0 100644 --- a/carbon-components-ember/.gitignore +++ b/carbon-components-ember/.gitignore @@ -3,11 +3,11 @@ dist/ dist-tests/ declarations/ -# generated by `pnpm create-autogenerated-files` +# generated by `pnpm generate` src/components/icons/ unpublished-development-types/carbon-icons.d.ts -# generated by `pnpm create-skill-references` from the Storybook build +# generated by `pnpm generate:skill-references` from the Storybook build skills/carbon-components-ember/references/components.md skills/carbon-components-ember/references/components/ diff --git a/carbon-components-ember/.prettierignore b/carbon-components-ember/.prettierignore index 48f31c21a7..cd419dfe7b 100644 --- a/carbon-components-ember/.prettierignore +++ b/carbon-components-ember/.prettierignore @@ -7,7 +7,7 @@ /declarations/ /storybook-static/ -# generated by `pnpm create-autogenerated-files` +# generated by `pnpm generate` /src/components/icons/ /src/components.ts /src/ai-chat.ts diff --git a/carbon-components-ember/.storybook/manifests.ts b/carbon-components-ember/.storybook/manifests.ts index add129031d..3abb7270dd 100644 --- a/carbon-components-ember/.storybook/manifests.ts +++ b/carbon-components-ember/.storybook/manifests.ts @@ -9,7 +9,7 @@ import { readFileSync } from 'node:fs'; import type { Manifests } from 'storybook/internal/types'; -// Written by `pnpm create-autogenerated-files`. +// Written by `pnpm generate`. const BARRELS = { components: '../src/components.ts', 'ai-chat': '../src/ai-chat.ts', diff --git a/carbon-components-ember/eslint.config.mjs b/carbon-components-ember/eslint.config.mjs index 0bba2ef803..71e1f45afe 100644 --- a/carbon-components-ember/eslint.config.mjs +++ b/carbon-components-ember/eslint.config.mjs @@ -42,7 +42,7 @@ export default defineConfig([ 'coverage/', 'storybook-static/', '!**/.*', - // generated by `pnpm create-autogenerated-files` + // generated by `pnpm generate` 'src/components/icons/', ]), js.configs.recommended, diff --git a/carbon-components-ember/package.json b/carbon-components-ember/package.json index 2c8d76f177..420accfbd5 100644 --- a/carbon-components-ember/package.json +++ b/carbon-components-ember/package.json @@ -38,10 +38,12 @@ ], "scripts": { "build": "rollup --config", - "build:skill": "storybook build --quiet && pnpm create-skill-references", + "build:skill": "storybook build --quiet && pnpm generate:skill-references", "build:watch": "rollup --config --watch --no-watch.clearScreen", - "create-autogenerated-files": "node ./scripts/create-files.mjs", - "create-skill-references": "node ./scripts/create-skill-references.mjs", + "generate": "pnpm generate:icons && pnpm generate:barrels", + "generate:barrels": "node ./scripts/barrels.mjs", + "generate:icons": "node ./scripts/icons.mjs", + "generate:skill-references": "node ./scripts/skill-references.mjs", "format": "prettier . --cache --write", "lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\" --prefixColors auto", "lint:fix": "concurrently \"pnpm:lint:*:fix\" --names \"fix:\" --prefixColors auto && pnpm run format", @@ -50,7 +52,7 @@ "lint:js:fix": "eslint . --fix", "lint:publish": "pnpm build && publint run --level error", "lint:types": "ember-tsc --noEmit", - "prepack": "pnpm create-autogenerated-files && rollup --config && pnpm build:skill", + "prepack": "pnpm generate && rollup --config && pnpm build:skill", "start": "vite dev", "test": "vite build --mode=development --out-dir dist-tests && testem --file testem.cjs ci --port 0", "test:update-snapshots": "vite build --mode=development --out-dir dist-tests && UPDATE_SNAPSHOTS=true testem --file testem.cjs ci --port 0", diff --git a/carbon-components-ember/scripts/barrels.mjs b/carbon-components-ember/scripts/barrels.mjs new file mode 100644 index 0000000000..e7ec612b8d --- /dev/null +++ b/carbon-components-ember/scripts/barrels.mjs @@ -0,0 +1,101 @@ +/** + * Writes the barrels: src/components.ts, src/ai-chat.ts, src/charts.ts and + * src/icons.ts, which apps import as `carbon-components-ember/components`, + * `/ai-chat`, `/charts` and `/icons` (dist/*.js, via the package.json `./*` + * export). Run it after icons.mjs, which writes the icon components; + * `pnpm generate` runs both. + */ + +import fs from 'node:fs'; +import path from 'node:path'; + +function createIndexFiles() { + // Sorted, so the generated barrels are the same on every OS. + const files = fs.readdirSync('./src', { recursive: true }).sort(); + const groups = { + components: [], + aiChat: [], + charts: [], + icons: [], + }; + for (const addonFilename of files) { + if (!addonFilename.includes('.')) continue; + if (addonFilename.includes('.gitkeep')) continue; + if (/\.stories\.[gt]?[jt]s$|\.mdx$/.test(addonFilename)) continue; + if (addonFilename.includes('render-svg-part.ts')) continue; + if ( + addonFilename.startsWith('components/icons') && + !addonFilename.includes('/-') + ) { + groups.icons.push(addonFilename); + continue; + } + if ( + addonFilename.startsWith('components/') && + !addonFilename.includes('/-') + ) { + // AI Chat and the charts have their own barrels, as upstream's are + // packages of their own (`@carbon/ai-chat-components`, + // `@carbon/charts-react`), so AI Chat keeps upstream's names (`Card`, + // `CodeSnippet`). + const group = addonFilename.startsWith('components/ai-chat/') + ? groups.aiChat + : addonFilename.startsWith('components/charts/') + ? groups.charts + : groups.components; + group.push(addonFilename); + } + } + if (groups.icons.length === 0) { + throw new Error( + 'src/components/icons/ has no icon components; run scripts/icons.mjs first', + ); + } + writeBarrel(groups.components, './src/components.ts'); + writeBarrel(groups.aiChat, './src/ai-chat.ts'); + writeBarrel(groups.charts, './src/charts.ts'); + writeBarrel(groups.icons, './src/icons.ts'); +} + +/** + * Writes a barrel exporting each component under its file name in + * PascalCase (`text-input.gts` is `TextInput`), so name the file after the + * export. A top-level `ai-` or `ui-` file spells the acronym as Carbon React + * does (`AILabel`, `UIShell`). + */ +function writeBarrel(components, file) { + const lines = []; + const exportedFrom = new Map(); + for (const comp of components) { + const camelCased = path + .basename(comp) + .replace(/\..*$/, '') + .split('-') + .map((word, i) => + i === 0 && /^(ai|ui)$/.test(word) && path.dirname(comp) === 'components' + ? word.toUpperCase() + : word[0].toUpperCase() + word.slice(1), + ) + .join(''); + if (exportedFrom.has(camelCased)) { + throw new Error( + `${comp} and ${exportedFrom.get(camelCased)} would both export ${camelCased} from ${file}; rename one`, + ); + } + exportedFrom.set(camelCased, comp); + const relativePath = comp.replace('components/', ''); + const source = fs + .readFileSync(path.join('./src/components', relativePath)) + .toString(); + const namedExports = [ + ...source.matchAll(/^export (?:class|const|function) ([A-Za-z0-9_]+)/gm), + ].map((m) => m[1]); + const specifiers = [`default as ${camelCased}`, ...namedExports]; + lines.push( + `export { ${specifiers.join(', ')} } from './components/${relativePath}'`, + ); + } + fs.writeFileSync(file, lines.join('\n')); +} + +createIndexFiles(); diff --git a/carbon-components-ember/scripts/create-files.mjs b/carbon-components-ember/scripts/create-files.mjs deleted file mode 100644 index 649557cf0a..0000000000 --- a/carbon-components-ember/scripts/create-files.mjs +++ /dev/null @@ -1,193 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -function createIndexFiles() { - // Sorted, so the generated barrels are the same on every OS. - const files = fs.readdirSync('./src', { recursive: true }).sort(); - const groups = { - components: [], - aiChat: [], - charts: [], - icons: [], - }; - for (const addonFilename of files) { - if (!addonFilename.includes('.')) continue; - if (addonFilename.includes('.gitkeep')) continue; - if (/\.stories\.[gt]?[jt]s$|\.mdx$/.test(addonFilename)) continue; - if (addonFilename.includes('render-svg-part.ts')) continue; - if ( - addonFilename.startsWith('components/icons') && - !addonFilename.includes('/-') - ) { - groups.icons.push(addonFilename); - continue; - } - if ( - addonFilename.startsWith('components/') && - !addonFilename.includes('/-') - ) { - // AI Chat and the charts have their own barrels, as upstream's are - // packages of their own (`@carbon/ai-chat-components`, - // `@carbon/charts-react`), so AI Chat keeps upstream's names (`Card`, - // `CodeSnippet`). - const group = addonFilename.startsWith('components/ai-chat/') - ? groups.aiChat - : addonFilename.startsWith('components/charts/') - ? groups.charts - : groups.components; - group.push(addonFilename); - } - } - // `carbon-components-ember/components`, `/ai-chat` and `/charts` - // (dist/components.js etc., via the package.json `./*` export). - writeBarrel(groups.components, './src/components.ts'); - writeBarrel(groups.aiChat, './src/ai-chat.ts'); - writeBarrel(groups.charts, './src/charts.ts'); - - const iconsIndexFile = []; - for (const comp of groups.icons) { - let camelCased = comp - .split('/') - .at(-1) - .split('.') - .at() - .replace(/-([a-zA-Z])/g, function (g) { - return g[1].toUpperCase(); - }); - camelCased = camelCased[0].toUpperCase() + camelCased.slice(1); - iconsIndexFile.push(`export { default as ${camelCased} } from './${comp}'`); - } - fs.writeFileSync('./src/icons.ts', iconsIndexFile.join('\n')); -} - -/** - * Writes a barrel exporting each component under its file name in - * PascalCase (`text-input.gts` is `TextInput`), so name the file after the - * export. A top-level `ai-` or `ui-` file spells the acronym as Carbon React - * does (`AILabel`, `UIShell`). - */ -function writeBarrel(components, file) { - const lines = []; - const exportedFrom = new Map(); - for (const comp of components) { - const camelCased = path - .basename(comp) - .replace(/\..*$/, '') - .split('-') - .map((word, i) => - i === 0 && /^(ai|ui)$/.test(word) && path.dirname(comp) === 'components' - ? word.toUpperCase() - : word[0].toUpperCase() + word.slice(1), - ) - .join(''); - if (exportedFrom.has(camelCased)) { - throw new Error( - `${comp} and ${exportedFrom.get(camelCased)} would both export ${camelCased} from ${file}; rename one`, - ); - } - exportedFrom.set(camelCased, comp); - const relativePath = comp.replace('components/', ''); - const source = fs - .readFileSync(path.join('./src/components', relativePath)) - .toString(); - const namedExports = [ - ...source.matchAll(/^export (?:class|const|function) ([A-Za-z0-9_]+)/gm), - ].map((m) => m[1]); - const specifiers = [`default as ${camelCased}`, ...namedExports]; - lines.push( - `export { ${specifiers.join(', ')} } from './components/${relativePath}'`, - ); - } - fs.writeFileSync(file, lines.join('\n')); -} - -function createIconIndex() { - const iconsIndex = path.resolve('./node_modules/@carbon/icons/es/index.js'); - const content = fs.readFileSync(iconsIndex).toString(); - - // Step 1: parse all import lines → varName → relative path (without .js) - const varToPath = {}; - for (const m of content.matchAll(/^import (\S+) from "\.\/([^"]+)";$/gm)) { - varToPath[m[1]] = m[2].replace('.js', ''); - } - - // Step 2: parse export block → varName → export name - // The export block is a single line: export { var1 as Name1, var2 as Name2, ... } - const varToExport = {}; - for (const m of content.matchAll(/\b(\S+?) as ([A-Za-z]\w*)/g)) { - varToExport[m[1]] = m[2]; - } - - // Step 3: build matchList as [exportName, '@carbon/icons/es/path/size'] - const matchList = []; - for (const [varName, exportName] of Object.entries(varToExport)) { - const iconPath = varToPath[varName]; - if (iconPath) { - matchList.push([exportName, '@carbon/icons/es/' + iconPath]); - } - } - - let typesContent = ` -type Icon = { - name: IconNames; - elem: string; - attrs: Record; - content: { - elem: string; - attrs: Record; - }[]; - size: number; -}; - `; - const icons = {}; - const iconNames = {}; - for (const [exportName, fullPath] of matchList) { - const size = fullPath.split('/').at(-1); - const commonPath = fullPath.split('/').slice(0, -1).join('/'); - icons[commonPath] = icons[commonPath] || []; - icons[commonPath].push(size); - iconNames[commonPath] = exportName.replace(size, '').replace('Glyph', ''); - } - // Start clean, so icons renamed or removed upstream don't leave stale files. - fs.rmSync('./src/components/icons', { recursive: true, force: true }); - fs.mkdirSync('./src/components/icons', { recursive: true }); - fs.writeFileSync('./src/components/icons/.gitkeep', 'auto generated icons\n'); - for (const [icon, sizes] of Object.entries(icons)) { - const iconSizeMap = sizes - .map( - (size) => - `'${size}': new TrackedPromise(() => import('${icon}/${size}')),`, - ) - .join('\n '); - const index = sizes.includes('index') ? 'index' : '32'; - const content = ` -import Icon from '../icon.gts'; -import { TrackedPromise } from '../../utils/tracked.ts'; - -const icons = { - ${iconSizeMap} -}; - -export default class ${iconNames[icon]} extends Icon { - get svg() { - return (icons[(this.args.size || 24).toString() as keyof typeof icons] || icons['${index}']).getValue()?.default; - } -} - `; - let dashed = iconNames[icon] - .replace(/[A-Z]/g, (m) => '-' + m.toLowerCase()) - .replace(/^-/, ''); - fs.writeFileSync(`./src/components/icons/${dashed}.ts`, content); - } - for (const [, fullPath] of matchList) { - typesContent += '\n'; - typesContent += `declare module '${fullPath}' { export default {} as Icon };\n`; - } - fs.writeFileSync( - './unpublished-development-types/carbon-icons.d.ts', - typesContent, - ); -} - -createIconIndex(); -createIndexFiles(); diff --git a/carbon-components-ember/scripts/icons.mjs b/carbon-components-ember/scripts/icons.mjs new file mode 100644 index 0000000000..64b2200025 --- /dev/null +++ b/carbon-components-ember/scripts/icons.mjs @@ -0,0 +1,99 @@ +/** + * Writes an Ember component for each `@carbon/icons` icon into + * src/components/icons/, and the module declarations for their SVG data + * into unpublished-development-types/carbon-icons.d.ts. barrels.mjs then + * exports them from `carbon-components-ember/icons`. + */ + +import fs from 'node:fs'; +import path from 'node:path'; + +function createIconComponents() { + const iconsIndex = path.resolve('./node_modules/@carbon/icons/es/index.js'); + const content = fs.readFileSync(iconsIndex).toString(); + + // Step 1: parse all import lines → varName → relative path (without .js) + const varToPath = {}; + for (const m of content.matchAll(/^import (\S+) from "\.\/([^"]+)";$/gm)) { + varToPath[m[1]] = m[2].replace('.js', ''); + } + + // Step 2: parse export block → varName → export name + // The export block is a single line: export { var1 as Name1, var2 as Name2, ... } + const varToExport = {}; + for (const m of content.matchAll(/\b(\S+?) as ([A-Za-z]\w*)/g)) { + varToExport[m[1]] = m[2]; + } + + // Step 3: build matchList as [exportName, '@carbon/icons/es/path/size'] + const matchList = []; + for (const [varName, exportName] of Object.entries(varToExport)) { + const iconPath = varToPath[varName]; + if (iconPath) { + matchList.push([exportName, '@carbon/icons/es/' + iconPath]); + } + } + + let typesContent = ` +type Icon = { + name: IconNames; + elem: string; + attrs: Record; + content: { + elem: string; + attrs: Record; + }[]; + size: number; +}; + `; + const icons = {}; + const iconNames = {}; + for (const [exportName, fullPath] of matchList) { + const size = fullPath.split('/').at(-1); + const commonPath = fullPath.split('/').slice(0, -1).join('/'); + icons[commonPath] = icons[commonPath] || []; + icons[commonPath].push(size); + iconNames[commonPath] = exportName.replace(size, '').replace('Glyph', ''); + } + // Start clean, so icons renamed or removed upstream don't leave stale files. + fs.rmSync('./src/components/icons', { recursive: true, force: true }); + fs.mkdirSync('./src/components/icons', { recursive: true }); + fs.writeFileSync('./src/components/icons/.gitkeep', 'auto generated icons\n'); + for (const [icon, sizes] of Object.entries(icons)) { + const iconSizeMap = sizes + .map( + (size) => + `'${size}': new TrackedPromise(() => import('${icon}/${size}')),`, + ) + .join('\n '); + const index = sizes.includes('index') ? 'index' : '32'; + const content = ` +import Icon from '../icon.gts'; +import { TrackedPromise } from '../../utils/tracked.ts'; + +const icons = { + ${iconSizeMap} +}; + +export default class ${iconNames[icon]} extends Icon { + get svg() { + return (icons[(this.args.size || 24).toString() as keyof typeof icons] || icons['${index}']).getValue()?.default; + } +} + `; + let dashed = iconNames[icon] + .replace(/[A-Z]/g, (m) => '-' + m.toLowerCase()) + .replace(/^-/, ''); + fs.writeFileSync(`./src/components/icons/${dashed}.ts`, content); + } + for (const [, fullPath] of matchList) { + typesContent += '\n'; + typesContent += `declare module '${fullPath}' { export default {} as Icon };\n`; + } + fs.writeFileSync( + './unpublished-development-types/carbon-icons.d.ts', + typesContent, + ); +} + +createIconComponents(); diff --git a/carbon-components-ember/scripts/create-skill-references.mjs b/carbon-components-ember/scripts/skill-references.mjs similarity index 98% rename from carbon-components-ember/scripts/create-skill-references.mjs rename to carbon-components-ember/scripts/skill-references.mjs index b74b383f81..ed01f92f71 100644 --- a/carbon-components-ember/scripts/create-skill-references.mjs +++ b/carbon-components-ember/scripts/skill-references.mjs @@ -4,7 +4,7 @@ * and `references/components/.md` holds one page's import, arguments, * blocks and examples, as the components manifest has them. * - * Usage: node scripts/create-skill-references.mjs [storybook-static] + * Usage: node scripts/skill-references.mjs [storybook-static] */ import fs from 'node:fs';