diff --git a/README.md b/README.md index d363b0ec2..3801a1d7f 100644 --- a/README.md +++ b/README.md @@ -248,4 +248,4 @@ Bulma framework!_ ## License -MIT © [Alex Smith](https://github.com/allxsmith) +Source code licensed MIT diff --git a/bestax-mcp/README.md b/bestax-mcp/README.md index 3cb7a7cd2..1b1cc9e04 100644 --- a/bestax-mcp/README.md +++ b/bestax-mcp/README.md @@ -96,4 +96,4 @@ One per skill, so a task can start from the library's own guidance: - [Documentation](https://bestax.io) - [GitHub](https://github.com/allxsmith/bestax) -MIT © Alex Smith +Source code licensed MIT diff --git a/bestax-mcp/data/skills.json b/bestax-mcp/data/skills.json index 2f7959adf..4c77c280c 100644 --- a/bestax-mcp/data/skills.json +++ b/bestax-mcp/data/skills.json @@ -81,24 +81,43 @@ }, { "name": "bestax-migrate", - "description": "Migrate an existing app from react-bulma-components (v4) to @allxsmith/bestax-bulma on Bulma v1 — run the bestax-migrate codemod, then resolve every TODO(bestax-migrate) comment it leaves using the mapping references. Use when a repo imports react-bulma-components and wants to move to bestax-bulma, when TODO(bestax-migrate) comments are present in a codebase, or when asked to migrate off an unmaintained React Bulma library.", + "description": "Migrate an existing app from an unmaintained React Bulma library — react-bulma-components (v4) or rbx (v2) — to @allxsmith/bestax-bulma on Bulma v1. Run the bestax-migrate codemod, then resolve every TODO(bestax-migrate) comment it leaves using the per-source mapping references. Use when a repo imports react-bulma-components or rbx and wants to move to bestax-bulma, when TODO(bestax-migrate) comments are present in a codebase, or when asked to migrate off an unmaintained React Bulma library.", "promptName": "migrate", "dir": "bestax-migrate", "references": [ { - "id": "component-map", - "file": "references/component-map.md", + "id": "css-migration", + "file": "references/css-migration.md", + "bytes": 8555 + }, + { + "id": "rbx-component-map", + "file": "references/rbx/component-map.md", + "bytes": 12252 + }, + { + "id": "rbx-prop-map", + "file": "references/rbx/prop-map.md", + "bytes": 7973 + }, + { + "id": "rbx-unmappables", + "file": "references/rbx/unmappables.md", + "bytes": 11237 + }, + { + "id": "react-bulma-components-component-map", + "file": "references/react-bulma-components/component-map.md", "bytes": 13190 }, { - "id": "css-migration", - "file": "references/css-migration.md", - "bytes": 7999 + "id": "react-bulma-components-prop-map", + "file": "references/react-bulma-components/prop-map.md", + "bytes": 6363 }, - { "id": "prop-map", "file": "references/prop-map.md", "bytes": 6363 }, { - "id": "unmappables", - "file": "references/unmappables.md", + "id": "react-bulma-components-unmappables", + "file": "references/react-bulma-components/unmappables.md", "bytes": 4861 } ], diff --git a/bestax-migrate/CLAUDE.md b/bestax-migrate/CLAUDE.md index 662f7b5f8..ac2ab50b5 100644 --- a/bestax-migrate/CLAUDE.md +++ b/bestax-migrate/CLAUDE.md @@ -2,19 +2,37 @@ jscodeshift-based CLI (`pnpm dlx bestax-migrate `) that migrates existing apps from other React Bulma libraries to `@allxsmith/bestax-bulma`. Multi-source by design: -each source library registers in `src/sources/registry.ts`; the first is -`react-bulma-components` (v4 only). +each source library registers in `src/sources/registry.ts`. Two are shipped — +`react-bulma-components` (v4 only) and `rbx` (v2 only). ## Hard rules -- **react-bulma-components is NEVER installed anywhere in this repo** (supply-chain - policy). Fixtures are read as _text_ (`__testfixtures__`, `fixtures/kitchen-sink`); the - migration _input_ is never typechecked or executed. Validation typechecks the migrated - _output_ against the workspace `@allxsmith/bestax-bulma`. -- Mapping-table first: `src/sources/react-bulma-components/mapping.ts` is the single source - of truth. Every RBC v4 export must have an entry (`mapped`/`partial`/`todo`) — the - `mapping-coverage` test walks the vendored `RBC_EXPORTS` list against it. New coverage is - a table edit (plus a `special` handler in `specials.ts` when structure changes). +- **No source library is EVER installed anywhere in this repo** (supply-chain policy) — + not react-bulma-components, not rbx. Fixtures are read as _text_ (`__testfixtures__`, + `fixtures/kitchen-sink`, `fixtures/rbx-kitchen-sink`); the migration _input_ is never + typechecked or executed. Validation typechecks the migrated _output_ against the + workspace `@allxsmith/bestax-bulma`. +- Mapping-table first: each source's `mapping.ts` is its single source of truth. Every + export of that library must have an entry (`mapped`/`partial`/`todo`) — the + `mapping-coverage` test walks the vendored `RBC_EXPORTS` / `RBX_EXPORTS` list against it, + in both directions, so the table cannot grow an entry for something the library never + exported either. New coverage is a table edit (plus a `special` handler in `specials.ts` + when structure changes). +- **A new source is a directory plus one registry line.** `src/sources/_shared/` holds + everything library-agnostic: `jsx-utils.ts` (AST helpers + `TransformContext`), + `props.ts` (the `PropAction` interpreter — its universal table is a parameter, not an + import), `imports.ts` (binding collection and import aliasing), `specials-utils.ts` + (`alignTarget`, `mergeClassName`, `parseIconClasses`, and the `stripModifierProps` + factory), and `make-styles-transform.ts` (the whole Bulma 0.9→v1 stylesheet transform, + parameterised by the source package's own specifiers). What stays per-source is the data + — `mapping.ts`, `specials.ts`, `responsive.ts`, `deps.ts` — plus a `transform.ts` that + orchestrates them. +- `''` must be added to `MIGRATE_SOURCE_VALUES` in `telemetry-worker/src/schema.ts` + or its events are dropped at ingest; `check:conformance --only=telemetry-allowlists` + fails until it is. That worker deploys to production on merge, so the two land together. + The same check regex-scrapes each source's `index.ts` for + `: MigrationSource = { … name: '…' }` — a declaration that doesn't match that shape is a + hard violation, not a skip. - Anything unsafe gets a `// TODO(bestax-migrate): ` comment on the enclosing statement + a report entry — never a silent skip, never a best-guess rewrite of dynamic values. @@ -58,12 +76,20 @@ each source library registers in `src/sources/registry.ts`; the first is supported ones go in the other fixture files, which must stay TODO-free. - Fixtures and `.e2e-tmp` are excluded from tsc/eslint/prettier at both root and package level (root `eslint.config.js` + both `.prettierignore`s) — keep them that way. -- Real-world corpus: `pnpm --filter bestax-migrate validate:corpus` fetches the - react-bulma-components repo's own MIT-licensed Storybook stories (pinned SHA, text only, - into `.e2e-tmp/`) and scores the transform over them — fails on any crash or - unknown-component TODO. Deliberately NOT in CI (no third-party fetches in the pipeline); - run it before releases and after mapping changes, and eyeball the before/after diffs it - writes to `.e2e-tmp/corpus-out/`. +- Real-world corpus, one script per source, both fetching a pinned SHA as text only into + `.e2e-tmp/` and failing on any crash or unknown-component TODO. Deliberately NOT in CI + (no third-party fetches in the pipeline); run them before releases and after mapping + changes, and eyeball the before/after diffs they write: + - `validate:corpus` — react-bulma-components' own MIT Storybook stories → + `.e2e-tmp/corpus-out/`. + - `validate:corpus:rbx` — rbx's own MIT docs. rbx used **docz**, not Storybook, so the + script extracts the `` blocks out of its 43 `*.docs.mdx` pages (254 blocks) + and rebuilds each page as one synthetic module written the way a consumer writes rbx. + Output lands in `.e2e-tmp/corpus-out-rbx/`. + + The corpus is the only check that sees breadth; the kitchen-sink e2e is the only one that + sees bestax's _real_ prop names. Both are needed — the rbx e2e's typecheck caught six + mapping errors that 254 clean Playgrounds had not. ## Releases diff --git a/bestax-migrate/README.md b/bestax-migrate/README.md index d04c3782b..8a74f058d 100644 --- a/bestax-migrate/README.md +++ b/bestax-migrate/README.md @@ -13,9 +13,12 @@ Codemods that migrate existing React apps to [`@allxsmith/bestax-bulma`](https:/ Currently supported source libraries: -| Source | Status | -| -------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -| [`react-bulma-components`](https://github.com/couds/react-bulma-components) (v4) | ✅ All 32 components mapped (a few patterns are flagged as TODOs) | +| Source | Status | +| -------------------------------------------------------------------------------- | ------------------------------------------------------------------- | +| [`react-bulma-components`](https://github.com/couds/react-bulma-components) (v4) | ✅ All 32 components mapped (a few patterns are flagged as TODOs) | +| [`rbx`](https://github.com/dfee/rbx) (v2) | ✅ Full export surface mapped (a few patterns are flagged as TODOs) | + +Pass the source library as the first argument: `bestax-migrate `. ## Requirements @@ -30,6 +33,8 @@ the app being migrated — the source is read as text and is never executed. ```bash # Preview what would change (no writes) pnpm dlx bestax-migrate react-bulma-components src/ --dry +# …or, for an rbx app +pnpm dlx bestax-migrate rbx src/ --dry # Apply the migration pnpm dlx bestax-migrate react-bulma-components src/ @@ -39,15 +44,15 @@ npm and yarn work too: `npx bestax-migrate …` / `yarn dlx bestax-migrate …`. The codemod uses [jscodeshift](https://github.com/facebook/jscodeshift) to: -- rewrite `react-bulma-components` imports to `@allxsmith/bestax-bulma` (including `const { Input } = Form` destructuring and namespace imports) +- rewrite the source library's imports to `@allxsmith/bestax-bulma` (including destructuring and namespace imports) - rename components and compound sub-components (`Form.Textarea` → `TextArea`, `Card.Footer.Item` → `Card.FooterItem`, `Hero.Footer` → `Hero.Foot`, …) - convert props (`renderAs` → `as`, `loading` → `isLoading`, numeric spacing/text sizes → string unions, `textAlign="center"` → `textAlign="centered"`, …) - flatten responsive breakpoint objects (`mobile={{ size: 4 }}` → `sizeMobile={4}`) - restructure patterns bestax models differently (`Table.Container`, Navbar dropdowns, `Form.Help`, icon-font children → ``) - migrate stylesheets: CSS imports converge on `@allxsmith/bestax-bulma/bestax.css`, and SCSS files move from Bulma 0.9's `@import` + `$var !default` overrides to `@use 'bulma/sass' with (…)` plus `@use '@allxsmith/bestax-bulma/scss/extras'` -- update `package.json`: remove `react-bulma-components`, add `@allxsmith/bestax-bulma`, bump `bulma` to `^1`, and swap the dead `node-sass` for dart `sass` (no install is ever run) +- update `package.json`: remove the source library, add `@allxsmith/bestax-bulma`, raise a declared pre-1.0 `bulma` to `^1` (adding it only when your sources import `bulma/…` directly — otherwise it arrives transitively), and swap the dead `node-sass` for dart `sass` (no install is ever run). Migrating **rbx** is what frees an app from Bulma 0.7.5: rbx pinned it as a _direct_ dependency, so the app could not choose its own Bulma version at all. The four Bulma extensions rbx also pulled in (`bulma-badge`, `bulma-divider`, `bulma-pageloader`, `bulma-tooltip`) are **reported rather than removed** — bestax replaces all four, but a manifest entry is a deliberate declaration and your own Sass may import them -Anything without a safe automatic conversion is left in place with a `// TODO(bestax-migrate): …` comment, and the run ends with a report of every TODO by file and line. TODOs are expected output, not errors — resolve them with the [migration guide](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components), or let the [`bestax-migrate` Agent Skill](https://bestax.io/docs/skills/intro) walk them for you: +Anything without a safe automatic conversion is left in place with a `// TODO(bestax-migrate): …` comment, and the run ends with a report of every TODO by file and line. TODOs are expected output, not errors — resolve them with the migration guide ([react-bulma-components](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components), [rbx](https://bestax.io/docs/guides/getting-started/migration/rbx)), or let the [`bestax-migrate` Agent Skill](https://bestax.io/docs/skills/intro) walk them for you: ```bash npx skills add https://github.com/allxsmith/bestax --skill bestax-migrate @@ -69,7 +74,7 @@ npx skills add https://github.com/allxsmith/bestax --skill bestax-migrate 2. Search for `TODO(bestax-migrate)` and resolve each comment 3. Typecheck/build and review the rendered app -Full walkthrough: [react-bulma-components migration guide](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components). +Full walkthrough: [react-bulma-components migration guide](https://bestax.io/docs/guides/getting-started/migration/react-bulma-components) · [rbx migration guide](https://bestax.io/docs/guides/getting-started/migration/rbx). ## Hardened by default @@ -100,4 +105,4 @@ Full disclosure of every field and control: [Telemetry guide](https://bestax.io/ ## License -MIT © Alex Smith +Source code licensed MIT diff --git a/bestax-migrate/e2e/rbx-kitchen-sink.test.ts b/bestax-migrate/e2e/rbx-kitchen-sink.test.ts new file mode 100644 index 000000000..acc2f6c48 --- /dev/null +++ b/bestax-migrate/e2e/rbx-kitchen-sink.test.ts @@ -0,0 +1,326 @@ +/** + * End-to-end gate: copy the source-only rbx kitchen-sink app into .e2e-tmp/, + * run the codemod on every file, then typecheck the MIGRATED output against + * the real @allxsmith/bestax-bulma types (workspace dependency — bulma-ui + * must be built first; turbo orders this). + * + * rbx itself is never installed: the input app is never typechecked, and + * leftovers.tsx — the file that exercises everything the codemod + * intentionally refuses to convert — is excluded from the output typecheck + * and asserted through its TODO annotations instead. + */ + +import { spawnSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { rbx } from '../src/sources/rbx/index.js'; +import { runTransform } from '../src/runner.js'; +import type { TodoEntry } from '../src/types.js'; +import { compileMigratedScss } from './support/compile-scss.js'; + +const packageRoot = path.join( + path.dirname(fileURLToPath(import.meta.url)), + '..' +); +const fixtureDir = path.join(packageRoot, 'fixtures', 'rbx-kitchen-sink'); + +// One scratch directory PER PROCESS, not one shared path — see the comment in +// kitchen-sink.test.ts for why (`pnpm all` runs two jest processes over this +// file concurrently). BESTAX_E2E_KEEP=1 leaves it behind for inspection. +const tmpRoot = path.join(packageRoot, '.e2e-tmp'); +fs.mkdirSync(tmpRoot, { recursive: true }); +const tmpDir = fs.mkdtempSync(path.join(tmpRoot, 'rbx-kitchen-sink-')); + +interface MigratedApp { + todosByFile: Map; + files: string[]; + /** + * package.json entries are tracked separately: every `deps` entry is a + * report line describing what the updater changed, not a problem the user + * has to fix, so they must not count against the "no TODOs" assertion. + */ + depTodos: TodoEntry[]; +} + +function migrateKitchenSink(): MigratedApp { + fs.rmSync(tmpDir, { recursive: true, force: true }); + fs.cpSync(fixtureDir, tmpDir, { recursive: true }); + + const srcDir = path.join(tmpDir, 'src'); + const files = fs.readdirSync(srcDir).filter(f => /\.(tsx?|jsx?)$/.test(f)); + const todosByFile = new Map(); + + for (const file of files) { + const filePath = path.join(srcDir, file); + const source = fs.readFileSync(filePath, 'utf8'); + const todos: TodoEntry[] = []; + const { output } = runTransform(rbx.transform, file, source, { + add: entry => todos.push(entry), + }); + if (output !== null) fs.writeFileSync(filePath, output); + todosByFile.set(file, todos); + } + + const scssPath = path.join(srcDir, 'styles.scss'); + const scssTodos: TodoEntry[] = []; + const scssOut = rbx.transformStyles!( + 'styles.scss', + fs.readFileSync(scssPath, 'utf8'), + { add: entry => scssTodos.push(entry) }, + { cssMode: 'bestax' } + ); + if (scssOut !== null) fs.writeFileSync(scssPath, scssOut); + todosByFile.set('styles.scss', scssTodos); + + const pkgPath = path.join(tmpDir, 'package.json'); + const pkgTodos: TodoEntry[] = []; + const pkgNext = rbx.updateDependencies!( + 'package.json', + JSON.parse(fs.readFileSync(pkgPath, 'utf8')), + { add: entry => pkgTodos.push(entry) }, + { cssMode: 'bestax', bulmaReferenced: true } + ); + if (pkgNext !== null) { + fs.writeFileSync(pkgPath, `${JSON.stringify(pkgNext, null, 2)}\n`); + } + + return { todosByFile, files, depTodos: pkgTodos }; +} + +describe('rbx kitchen-sink e2e', () => { + let app: MigratedApp; + + beforeAll(() => { + app = migrateKitchenSink(); + }); + + afterAll(() => { + if (!process.env.BESTAX_E2E_KEEP) { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } + }); + + it('migrates every kitchen-sink file', () => { + expect(app.files.sort()).toEqual([ + 'App.tsx', + 'components.tsx', + 'elements.tsx', + 'form.tsx', + 'grid.tsx', + 'helpers.tsx', + 'layout.tsx', + 'leftovers.tsx', + ]); + for (const file of app.files) { + const migrated = fs.readFileSync(path.join(tmpDir, 'src', file), 'utf8'); + // Only leftovers.tsx may keep its (trimmed, TODO-annotated) rbx import. + if (file !== 'leftovers.tsx') { + expect({ file, hasRbx: /from ["']rbx["']/.test(migrated) }).toEqual({ + file, + hasRbx: false, + }); + } + } + }); + + it('leaves no TODOs outside leftovers.tsx, bar the Modal advisory', () => { + // `component:Modal` fires on EVERY conversion by design: bestax renders + // the modal inline and implements neither Escape-to-close nor scroll + // locking, all of which rbx did by default. The markup migrates cleanly — + // this file still typechecks — but the behaviour differs, and nothing + // else would tell the user. + for (const [file, todos] of app.todosByFile) { + if (file === 'leftovers.tsx') continue; + const unexpected = todos.filter(t => t.rule !== 'component:Modal'); + expect({ file, todos: unexpected }).toEqual({ file, todos: [] }); + } + const components = app.todosByFile.get('components.tsx') ?? []; + expect(components.every(t => t.rule === 'component:Modal')).toBe(true); + }); + + it('adopts the bestax combined CSS bundle in App.tsx', () => { + const migrated = fs.readFileSync( + path.join(tmpDir, 'src', 'App.tsx'), + 'utf8' + ); + expect(migrated).toContain('import "@allxsmith/bestax-bulma/bestax.css";'); + expect(migrated).not.toContain('rbx/index.css'); + }); + + it('turns rbx helper props into wrapping Badge and Tooltip components', () => { + const migrated = fs.readFileSync( + path.join(tmpDir, 'src', 'helpers.tsx'), + 'utf8' + ); + expect(migrated).toContain(''); + expect(migrated).toContain(' { + const scss = fs.readFileSync( + path.join(tmpDir, 'src', 'styles.scss'), + 'utf8' + ); + expect(scss).toContain("@use 'bulma/sass' with ("); + expect(scss).not.toContain('~rbx/rbx'); + }); + + it('compiles the migrated SCSS entry with Dart Sass', () => { + const scss = fs.readFileSync( + path.join(tmpDir, 'src', 'styles.scss'), + 'utf8' + ); + const { status, diagnostics } = compileMigratedScss(scss); + expect({ status, diagnostics }).toEqual({ status: 0, diagnostics: '' }); + }); + + it('deletes rbx, bumps Bulma, and reports the extensions', () => { + const pkg = JSON.parse( + fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf8') + ); + expect(pkg.dependencies.rbx).toBeUndefined(); + // The four extensions are reported, not deleted: a manifest entry is a + // deliberate declaration and the app's own Sass may import them. + expect(pkg.dependencies['bulma-badge']).toBe('^3.0.1'); + expect(pkg.dependencies['bulma-tooltip']).toBe('^2.0.2'); + expect(pkg.dependencies['@allxsmith/bestax-bulma']).toBe('^5'); + expect(pkg.dependencies.bulma).toBe('^1.0.4'); + expect(pkg.devDependencies['node-sass']).toBeUndefined(); + expect(pkg.devDependencies.sass).toBe('^1.79.0'); + }); + + it('reports the dependency changes and the React 16 peer gap', () => { + const messages = app.depTodos.map(t => `${t.rule}: ${t.message}`); + expect( + messages.some(m => /^deps: removed rbx and bumped bulma/.test(m)) + ).toBe(true); + expect( + messages.some(m => /are Bulma extensions rbx depended on/.test(m)) + ).toBe(true); + expect(messages.some(m => m.startsWith('peer-deps: react ^16.8.6'))).toBe( + true + ); + }); + + it('annotates every intentionally unsupported pattern in leftovers.tsx', () => { + const todos = app.todosByFile.get('leftovers.tsx') ?? []; + const rules = new Set(todos.map(t => t.rule)); + expect(rules).toContain('component:Tile'); + expect(rules).toContain('component:List'); + expect(rules).toContain('component:Generic'); + expect(rules).toContain('component:Fieldset'); + expect(rules).toContain('component:Numeric'); + expect(rules).toContain('component:Highlight'); + expect(rules).toContain('component:Icon'); + expect(rules).toContain('component:File.Label'); + expect(rules).toContain('component:Dropdown'); + expect(todos.length).toBeGreaterThanOrEqual(10); + const migrated = fs.readFileSync( + path.join(tmpDir, 'src', 'leftovers.tsx'), + 'utf8' + ); + expect(migrated).toContain('TODO(bestax-migrate)'); + // Unmappable components keep a trimmed, TODO-annotated rbx import. + expect(migrated).toMatch(/from ["']rbx["']/); + }); + + it('documents every TODO rule it emits in the skill reference', () => { + // A rule with no recipe is a dead end for the user reading the report. + // `component:Modal` shipped exactly that way — emitted on every Modal + // conversion, documented nowhere — and only a reviewer caught it. + const skillDir = path.join(packageRoot, '..', 'skills', 'bestax-migrate'); + const refsDir = path.join(skillDir, 'references', 'rbx'); + const refs = [ + ...fs + .readdirSync(refsDir) + .filter(f => f.endsWith('.md')) + .map(f => path.join(refsDir, f)), + path.join(skillDir, 'SKILL.md'), + ] + .map(f => fs.readFileSync(f, 'utf8')) + .join('\n'); + + const emitted = new Set(); + for (const todos of app.todosByFile.values()) { + for (const todo of todos) emitted.add(todo.rule); + } + for (const todo of app.depTodos) emitted.add(todo.rule); + expect(emitted.size).toBeGreaterThan(0); + + // Match documented rule TOKENS, not substrings. `refs.includes(rule)` + // let `component:List` pass off the text of `component:List.Item`, and + // the prop fallback matched any backticked occurrence of the word + // anywhere in prose — so the guard could pass while the rule it was + // meant to pin went undocumented. + const tokens = new Set( + [...refs.matchAll(/(?:component|prop):[A-Za-z][\w.*]*/g)].map(m => m[0]) + ); + // A prop rule may instead be written bare in a row that names the + // component beside it (`| \`expanded\` on \`Field\` |`). + const backticked = new Set( + [...refs.matchAll(/`([A-Za-z][\w.-]*)`/g)].map(m => m[1]) + ); + + const documented = (rule: string): boolean => { + if (tokens.has(rule)) return true; + if (rule.startsWith('component:')) { + // A dotted part may be covered by its parent's section, which the + // reference writes as `component:File.*` or as a section for the + // parent component itself. + const parent = rule.slice('component:'.length).split('.')[0]; + return ( + tokens.has(`component:${parent}.*`) || + tokens.has(`component:${parent}`) + ); + } + if (rule.startsWith('prop:')) { + return backticked.has(rule.slice('prop:'.length)); + } + return backticked.has(rule); + }; + + const undocumented = [...emitted].sort().filter(r => !documented(r)); + expect(undocumented).toEqual([]); + }); + + it('typechecks the migrated output against @allxsmith/bestax-bulma', () => { + fs.writeFileSync( + path.join(tmpDir, 'global.d.ts'), + "declare module '*.css';\ndeclare module '*.scss';\n" + ); + fs.writeFileSync( + path.join(tmpDir, 'tsconfig.json'), + JSON.stringify( + { + compilerOptions: { + strict: true, + noEmit: true, + jsx: 'react-jsx', + module: 'ESNext', + target: 'ES2022', + moduleResolution: 'bundler', + lib: ['ES2022', 'DOM', 'DOM.Iterable'], + skipLibCheck: true, + }, + include: ['src/**/*', 'global.d.ts'], + exclude: ['src/leftovers.tsx'], + }, + null, + 2 + ) + ); + const result = spawnSync('pnpm', ['exec', 'tsc', '-p', tmpDir], { + cwd: packageRoot, + encoding: 'utf8', + }); + const diagnostics = `${result.stdout ?? ''}\n${result.stderr ?? ''}`.trim(); + expect({ status: result.status, diagnostics }).toEqual({ + status: 0, + diagnostics: '', + }); + }, 120000); +}); diff --git a/bestax-migrate/fixtures/rbx-kitchen-sink/package.json b/bestax-migrate/fixtures/rbx-kitchen-sink/package.json new file mode 100644 index 000000000..c90e9d1fc --- /dev/null +++ b/bestax-migrate/fixtures/rbx-kitchen-sink/package.json @@ -0,0 +1,20 @@ +{ + "name": "rbx-kitchen-sink", + "private": true, + "version": "0.0.0", + "dependencies": { + "bulma": "0.7.5", + "bulma-badge": "^3.0.1", + "bulma-divider": "^0.2.0", + "bulma-pageloader": "^2.1.0", + "bulma-tooltip": "^2.0.2", + "rbx": "^2.2.0", + "react": "^16.8.6", + "react-dom": "^16.8.6", + "@fortawesome/react-fontawesome": "^0.1.4", + "@fortawesome/free-solid-svg-icons": "^5.9.0" + }, + "devDependencies": { + "node-sass": "^4.12.0" + } +} diff --git a/bestax-migrate/fixtures/rbx-kitchen-sink/src/App.tsx b/bestax-migrate/fixtures/rbx-kitchen-sink/src/App.tsx new file mode 100644 index 000000000..14de3cf7c --- /dev/null +++ b/bestax-migrate/fixtures/rbx-kitchen-sink/src/App.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import "rbx/index.css"; +import "./styles.scss"; +import { Elements } from "./elements"; +import { Components } from "./components"; +import { FormControls } from "./form"; +import { Layout } from "./layout"; +import { Grid } from "./grid"; +import { Helpers } from "./helpers"; + +export const App = () => ( + <> + + + + + + + +); diff --git a/bestax-migrate/fixtures/rbx-kitchen-sink/src/components.tsx b/bestax-migrate/fixtures/rbx-kitchen-sink/src/components.tsx new file mode 100644 index 000000000..4b5a8b91e --- /dev/null +++ b/bestax-migrate/fixtures/rbx-kitchen-sink/src/components.tsx @@ -0,0 +1,102 @@ +import React from "react"; +import { + Breadcrumb, + Card, + Level, + Media, + Menu, + Message, + Modal, + Navbar, + Pagination, + Panel, + Tab, +} from "rbx"; + +export const Components = () => ( + <> + + + Card + + + + Body + + Action + + + + Home + + Here + + + + Left + Middle + Right + + + Avatar + Content + Extra + + + Menu + + Active + Other + + + + Header + Body + + + + + + Title + + Body + Foot + + + + + + Brand + + + + + Start + + + + End + + + + + Prev + + 1 + + + Next + + + Panel + + All + Some + + Block + + + One + Two + + +); diff --git a/bestax-migrate/fixtures/rbx-kitchen-sink/src/elements.tsx b/bestax-migrate/fixtures/rbx-kitchen-sink/src/elements.tsx new file mode 100644 index 000000000..47970d38e --- /dev/null +++ b/bestax-migrate/fixtures/rbx-kitchen-sink/src/elements.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import { + Block, + Box, + Button, + Content, + Delete, + Loader, + Notification, + PageLoader, + Progress, + Table, + Tag, + Title, +} from "rbx"; + +export const Elements = () => ( + + + + Kitchen sink + + + Every mapped element + + +

Body copy

+
+ + + + + + + + tag + + + + + Careful + + + + + + + Name + + + + + Value + + + + + Total + + +
+
+
+); diff --git a/bestax-migrate/fixtures/rbx-kitchen-sink/src/form.tsx b/bestax-migrate/fixtures/rbx-kitchen-sink/src/form.tsx new file mode 100644 index 000000000..69e14ef79 --- /dev/null +++ b/bestax-migrate/fixtures/rbx-kitchen-sink/src/form.tsx @@ -0,0 +1,54 @@ +import React from "react"; +import { + Checkbox, + Control, + Field, + Help, + Input, + Label, + Radio, + Select, + Textarea, +} from "rbx"; + +export const FormControls = () => ( + <> + + + + + + + + + Required + + + + + + + + + + + +