mirror of
https://github.com/nocobase/nocobase.git
synced 2026-10-16 15:07:08 +08:00
* fix(ui-library): sync auth-ui, check items as plugins compile them, and document the library Bring the auth-ui item up to date with the copies the templates ship. Those gained translations in #264 while the registry source kept hard-coded English, so `shadcn add @nocobase/auth-ui` installed an older version than any template carried. The item now declares @nocobase/i18n. - Relative imports inside items carry the .js extension. A plugin compiles installed source with NodeNext, which rejects an extensionless import; an application's bundler resolves either form. - tsconfig.registry.json compiles the registry sources with NodeNext against the real workspace packages. tsconfig.json no longer maps the authentication plugin's actions to the preview's mock, so both programs check the items against the plugin's real exports and the mock is typed from them; only the Vite build swaps it in. A plugin change that breaks an item now fails typecheck in that plugin's pull request. - The package is linted with the rules a plugin uses. The preview's shadcn primitives get the relaxations the Portal configuration gives an application's, and the two problems the rules found in the preview, a synchronous setState in an effect and a fetch that was never aborted, are fixed. The UI Library workflow lints before it publishes, and the pre-commit hook lints the directory with its own configuration. - The preview mounts an i18n runtime, as an application does, so the translated items render without react-i18next warnings. - MAINTAINING.md and USAGE.md document maintaining the library and installing its items into applications and plugins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ui-library): ship auth-ui's translations with the item The item's strings were translatable, but the translations lived only in the templates' own locale files, so any other consumer had to copy the keys by hand and could not tell when an upgrade added one. - auth-ui ships locales/en-US.ts and locales/zh-CN.ts, listed in its files. en-US exports its shape as a type and zh-CN is annotated with it, so typecheck fails on a key either one lacks or adds. - The item's docs, README and the usage guide describe merging the files into the consumer's locale resources, which installing does not do, and MAINTAINING.md makes shipping every key a rule for items. - i18n:check also scans ui-library/registry/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * revert(ui-library): leave the i18n:check scan to #400 The change that adds ui-library/registry/ to i18n:check now lives in #400, next to the parser fix that makes it effective, so the two pull requests no longer edit the same lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ui-library): address review findings - The documented merge of an item's locales into a plugin's en-US.ts failed the plugin's declaration build with TS9015, because isolatedDeclarations cannot infer an object built with a spread. The item README now shows en-US annotated as `typeof authUi & typeof messages`, and USAGE.md says the merged file needs it. - The preview's theme effect toggled only the `dark` class, leaving the inline color-scheme index.html sets on load stale after a toggle; it now updates both. - ESLint relaxed only the first of dev-config's two shadcn registry blocks, so adding the chart primitive the documented way failed lint. dev-config now exports createShadcnRegistryConfig(root), which the Portal factory uses for client/ and ui-library uses for website/, so the list is no longer copied. - tsconfig.registry.json compiled items with the ESNext library and without noUnusedLocals, unlike the plugins that install them; it now uses the create-plugin template's lib and noUnusedLocals. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
3.7 KiB
JavaScript
84 lines
3.7 KiB
JavaScript
import path from 'node:path';
|
|
|
|
const CODE = /\.(?:[cm]?[jt]sx?)$/u;
|
|
const FORMATTABLE =
|
|
/\.(?:[cm]?[jt]sx?|json5?|jsonc|mdx?|ya?ml|css|scss|less|html)$/u;
|
|
|
|
/**
|
|
* Directories that lint themselves rather than being covered by the root ESLint configuration, given as
|
|
* repository-relative prefixes. A directory belongs here when it has its own `eslint.config.*` AND its own ESLint
|
|
* dependency, because both halves matter: the root configuration enumerates the package roots it applies to and
|
|
* matches nothing under such a directory, so linting its files from the root silently succeeds without running a
|
|
* single rule, and `docs` is on ESLint 9 while the root is on ESLint 10, so the binary has to come from there too.
|
|
*
|
|
* Everything under `packages/` is covered by the root configuration and must not be listed.
|
|
*/
|
|
const SELF_LINTING_DIRECTORIES = ['docs', 'ui-library'];
|
|
|
|
/**
|
|
* ESLint runs once per package rather than once for every staged file.
|
|
*
|
|
* The type-aware rules build a TypeScript program per file and hold it for the length of the run, so a single process
|
|
* grows with the size of the commit. A repository-wide change — a refactor that moves every package, a dependency bump
|
|
* that rewrites every manifest — exhausts the heap, and the hook dies with an allocation failure rather than a lint
|
|
* result. Splitting by package bounds it: each process exits and frees what it held before the next starts.
|
|
*
|
|
* Grouping costs nothing for an ordinary commit, which touches one or two packages and so produces one or two
|
|
* commands, the same as before.
|
|
*
|
|
* Prettier still takes every staged file in one command. It formats a file at a time with no cross-file analysis, so
|
|
* its memory does not scale with the size of the commit the way ESLint's does. It also needs no per-directory
|
|
* treatment: `docs` inherits the same shared Prettier configuration as everything else, and the root
|
|
* `.prettierignore` — which the command below passes explicitly — is what decides which of its files are skipped.
|
|
*/
|
|
const groupByPackage = (files) => {
|
|
const groups = new Map();
|
|
|
|
for (const file of files) {
|
|
const relative = path.relative(import.meta.dirname, file);
|
|
const segments = relative.split(path.sep);
|
|
// `packages/<category>/<name>` is a package. A self-linting directory is its own group, keyed by its prefix so
|
|
// that the command can be run from inside it. Anything else is grouped as the repository root, which the root
|
|
// ESLint configuration covers.
|
|
const key =
|
|
segments[0] === 'packages' && segments.length > 3
|
|
? segments.slice(0, 3).join('/')
|
|
: (SELF_LINTING_DIRECTORIES.find(
|
|
(directory) => segments[0] === directory,
|
|
) ?? '');
|
|
const group = groups.get(key);
|
|
if (group) group.push(file);
|
|
else groups.set(key, [file]);
|
|
}
|
|
|
|
return [...groups.entries()];
|
|
};
|
|
|
|
const quote = (values) =>
|
|
values.map((value) => JSON.stringify(value)).join(' ');
|
|
|
|
export default {
|
|
'*': (files) => {
|
|
const code = files.filter((file) => CODE.test(file));
|
|
const formattable = files.filter((file) => FORMATTABLE.test(file));
|
|
|
|
const commands = groupByPackage(code).map(([key, group]) => {
|
|
const lint = `eslint --fix --no-warn-ignored --max-warnings 0 ${quote(group)}`;
|
|
|
|
// A self-linting directory needs its own ESLint, resolved from its own node_modules and reading its own
|
|
// configuration. `pnpm --dir` runs the binary there while the paths stay absolute, so they resolve either way.
|
|
return SELF_LINTING_DIRECTORIES.includes(key)
|
|
? `pnpm --dir ${JSON.stringify(key)} exec ${lint}`
|
|
: lint;
|
|
});
|
|
|
|
if (formattable.length > 0) {
|
|
commands.push(
|
|
`prettier --write --ignore-path .prettierignore ${quote(formattable)}`,
|
|
);
|
|
}
|
|
|
|
return commands;
|
|
},
|
|
};
|