@bleedingdev/modern-js-code-tools 3.9.0-ultramodern.9 → 3.9.3-ultramodern.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/bin/modern-api-check-files.mjs +4 -1
  2. package/bin/modern-api-check.mjs +1 -1
  3. package/bin/modern-i18n-check.mjs +4 -0
  4. package/dist/cjs/cli/microvertical-api-check.cjs +36 -7
  5. package/dist/cjs/cli/workspace-source-check.cjs +90 -17
  6. package/dist/cjs/microvertical-api-baseline.cjs +81 -244
  7. package/dist/cjs/microvertical-api-boundary.cjs +352 -64
  8. package/dist/cjs/microvertical-api-owner.cjs +5 -2
  9. package/dist/cjs/module-graph.cjs +1862 -0
  10. package/dist/cjs/oxlint-plugin/rules/strict-effect-api-boundaries.cjs +1 -1
  11. package/dist/cjs/source-analysis.cjs +108 -0
  12. package/dist/cjs/strict-effect-runtime.cjs +75 -57
  13. package/dist/esm/cli/microvertical-api-check.js +26 -7
  14. package/dist/esm/cli/workspace-source-check.js +86 -13
  15. package/dist/esm/microvertical-api-baseline.js +66 -214
  16. package/dist/esm/microvertical-api-boundary.js +350 -65
  17. package/dist/esm/microvertical-api-owner.js +5 -2
  18. package/dist/esm/module-graph.js +1804 -0
  19. package/dist/esm/oxlint-plugin/rules/strict-effect-api-boundaries.js +1 -1
  20. package/dist/esm/source-analysis.js +48 -0
  21. package/dist/esm/strict-effect-runtime.js +75 -56
  22. package/dist/esm-node/cli/microvertical-api-check.js +26 -7
  23. package/dist/esm-node/cli/workspace-source-check.js +86 -13
  24. package/dist/esm-node/microvertical-api-baseline.js +66 -214
  25. package/dist/esm-node/microvertical-api-boundary.js +350 -65
  26. package/dist/esm-node/microvertical-api-owner.js +5 -2
  27. package/dist/esm-node/module-graph.js +1805 -0
  28. package/dist/esm-node/oxlint-plugin/rules/strict-effect-api-boundaries.js +1 -1
  29. package/dist/esm-node/source-analysis.js +49 -0
  30. package/dist/esm-node/strict-effect-runtime.js +75 -56
  31. package/dist/types/cli/microvertical-api-check.d.ts +1 -1
  32. package/dist/types/cli/workspace-source-check.d.ts +1 -1
  33. package/dist/types/microvertical-api-baseline.d.ts +0 -5
  34. package/dist/types/microvertical-api-boundary.d.ts +17 -2
  35. package/dist/types/microvertical-api-owner.d.ts +1 -1
  36. package/dist/types/module-graph.d.ts +100 -0
  37. package/dist/types/source-analysis.d.ts +13 -0
  38. package/package.json +17 -13
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { runMicroVerticalApiCheckCli } from '../dist/esm-node/cli/microvertical-api-check.js';
3
3
 
4
- process.exitCode = runMicroVerticalApiCheckCli(process.argv.slice(2), true);
4
+ process.exitCode = await runMicroVerticalApiCheckCli(
5
+ process.argv.slice(2),
6
+ true,
7
+ );
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
2
  import { runMicroVerticalApiCheckCli } from '../dist/esm-node/cli/microvertical-api-check.js';
3
3
 
4
- process.exitCode = runMicroVerticalApiCheckCli();
4
+ process.exitCode = await runMicroVerticalApiCheckCli();
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { runWorkspaceSourceCheckCli } from '../dist/esm-node/cli/workspace-source-check.js';
3
+
4
+ process.exitCode = runWorkspaceSourceCheckCli();
@@ -1,5 +1,14 @@
1
1
  "use strict";
2
2
  var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.n = (module)=>{
5
+ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
6
+ __webpack_require__.d(getter, {
7
+ a: getter
8
+ });
9
+ return getter;
10
+ };
11
+ })();
3
12
  (()=>{
4
13
  __webpack_require__.d = (exports1, getters, values)=>{
5
14
  var define = (defs, kind)=>{
@@ -30,30 +39,50 @@ __webpack_require__.r(__webpack_exports__);
30
39
  __webpack_require__.d(__webpack_exports__, {
31
40
  runMicroVerticalApiCheckCli: ()=>runMicroVerticalApiCheckCli
32
41
  });
42
+ const external_node_path_namespaceObject = require("node:path");
43
+ var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
44
+ const external_node_url_namespaceObject = require("node:url");
33
45
  const external_microvertical_api_boundary_cjs_namespaceObject = require("../microvertical-api-boundary.cjs");
34
- function runMicroVerticalApiCheckCli(args = process.argv.slice(2), filesOnly = false) {
46
+ const flags = [
47
+ '--workspace-root',
48
+ '--baseline-package-directory',
49
+ '--rules'
50
+ ];
51
+ async function loadSourceRules(file) {
52
+ const rules = (await import((0, external_node_url_namespaceObject.pathToFileURL)(external_node_path_default().resolve(file)).href)).default;
53
+ if (!Array.isArray(rules) || !rules.every((rule)=>'function' == typeof rule)) throw new Error(`${file}: default export must be an array of source rule functions, e.g. \`export default [myRule]\``);
54
+ return rules;
55
+ }
56
+ async function runMicroVerticalApiCheckCli(args = process.argv.slice(2), filesOnly = false) {
35
57
  let workspaceRoot = process.env.ULTRAMODERN_WORKSPACE_ROOT ?? process.cwd();
36
58
  let baselinePackageDirectory;
59
+ const ruleFiles = [];
37
60
  for(let index = 0; index < args.length; index += 1){
38
61
  const argument = args[index];
39
62
  if ('--help' === argument || '-h' === argument) {
40
- console.log(`${filesOnly ? 'modern-api-check-files' : 'modern-api-check'} [--workspace-root <path>] [--baseline-package-directory <path>]\n${filesOnly ? 'Checks consumer files, contracts, package exports and owner identity. Does not analyze runtime topology; run the strict Effect source phase separately.' : 'Checks consumer files, contracts, package exports, owner identity and runtime topology once per API entry.'}\nWorkspace defaults to ULTRAMODERN_WORKSPACE_ROOT then cwd. Exit codes: 0 valid, 1 consumer violation, 2 tool/configuration failure.`);
63
+ console.log(`${filesOnly ? 'modern-api-check-files' : 'modern-api-check'} [--workspace-root <path>] [--baseline-package-directory <path>] [--rules <module>]...\n${filesOnly ? 'Checks consumer files, contracts, package exports and owner identity. Does not analyze runtime topology; run the strict Effect source phase separately.' : 'Checks consumer files, contracts, package exports, owner identity and runtime topology once per API entry.'}\nThe baseline owner defaults to each contract's native package resolver. --baseline-package-directory requires that exact installed owner for every contract.\n--rules loads a module whose default export is an array of source rules; each rule runs once per workspace source file with the shared module graph.\nWorkspace defaults to ULTRAMODERN_WORKSPACE_ROOT then cwd. Exit codes: 0 valid, 1 consumer violation, 2 tool/configuration failure.`);
41
64
  return 0;
42
65
  }
43
- if (![
44
- '--workspace-root',
45
- '--baseline-package-directory'
46
- ].includes(argument ?? '') || !args[index + 1] || args[index + 1]?.startsWith('--')) {
66
+ if (!flags.includes(argument ?? '') || !args[index + 1] || args[index + 1]?.startsWith('--')) {
47
67
  console.error(`Invalid argument: ${argument ?? ''}. Use --help.`);
48
68
  return 2;
49
69
  }
50
70
  const value = args[++index];
51
71
  if ('--workspace-root' === argument) workspaceRoot = value;
72
+ else if ('--rules' === argument) ruleFiles.push(value);
52
73
  else baselinePackageDirectory = value;
53
74
  }
75
+ const sourceRules = [];
76
+ for (const file of ruleFiles)try {
77
+ sourceRules.push(...await loadSourceRules(file));
78
+ } catch (error) {
79
+ console.error(`API tool error: --rules ${error instanceof Error ? error.message : String(error)}`);
80
+ return 2;
81
+ }
54
82
  const result = (filesOnly ? external_microvertical_api_boundary_cjs_namespaceObject.checkMicroVerticalApiConsumerFiles : external_microvertical_api_boundary_cjs_namespaceObject.checkMicroVerticalApiBoundaries)({
55
83
  workspaceRoot,
56
- baselinePackageDirectory
84
+ baselinePackageDirectory,
85
+ sourceRules
57
86
  });
58
87
  for (const diagnostic of result.diagnostics)console.error(`API violation: ${diagnostic}`);
59
88
  for (const error of result.toolErrors)console.error(`API tool error: ${error}`);
@@ -37,8 +37,8 @@ var __webpack_require__ = {};
37
37
  var __webpack_exports__ = {};
38
38
  __webpack_require__.r(__webpack_exports__);
39
39
  __webpack_require__.d(__webpack_exports__, {
40
- main: ()=>main,
41
- runWorkspaceSourceCheck: ()=>runWorkspaceSourceCheck
40
+ runWorkspaceSourceCheck: ()=>runWorkspaceSourceCheck,
41
+ runWorkspaceSourceCheckCli: ()=>runWorkspaceSourceCheckCli
42
42
  });
43
43
  const external_node_fs_namespaceObject = require("node:fs");
44
44
  var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
@@ -50,6 +50,11 @@ const DEFAULT_LOCALES = [
50
50
  'en',
51
51
  'cs'
52
52
  ];
53
+ const DEFAULT_SOURCE_ROOTS = [
54
+ 'apps',
55
+ 'verticals',
56
+ 'packages'
57
+ ];
53
58
  const ignoredDirectories = new Set([
54
59
  '.modern',
55
60
  '.modernjs',
@@ -83,6 +88,8 @@ const createLocaleJsonMatcher = (locales)=>{
83
88
  const resolvePluralCategories = (locale, overrides)=>overrides?.[locale] ?? new Intl.PluralRules(locale).resolvedOptions().pluralCategories;
84
89
  const readText = (filePath)=>external_node_fs_default().readFileSync(filePath, 'utf-8');
85
90
  const localeImportPattern = (locale)=>new RegExp(`import\\s+(?:\\*\\s+as\\s+)?[A-Za-z_$][\\w$]*\\s+from\\s+['"]\\.\\./locales/${escapeRegExp(locale)}/[^'"]+\\.json['"]`, 'u');
91
+ class SourceViolation extends Error {
92
+ }
86
93
  const checkRuntimeResources = (root, filePath, text, locales)=>{
87
94
  const relative = relativePath(root, filePath);
88
95
  if (!relative.endsWith('/src/modern.runtime.ts')) return;
@@ -90,7 +97,7 @@ const checkRuntimeResources = (root, filePath, text, locales)=>{
90
97
  const registersResources = /initOptions\s*:\s*\{[\s\S]*?\bresources\s*[,:}]/u.test(text);
91
98
  if (missingLocales.length > 0 || !registersResources) {
92
99
  const detail = missingLocales.length > 0 ? `missing locale JSON imports for: ${missingLocales.join(', ')}` : 'initOptions does not register a `resources` entry';
93
- throw new Error(`${relative} must register locale JSON resources in modern.runtime.ts so Worker SSR and hydration use the same first-render translations (${detail}).`);
100
+ throw new SourceViolation(`${relative} must register locale JSON resources in modern.runtime.ts so Worker SSR and hydration use the same first-render translations (${detail}).`);
94
101
  }
95
102
  };
96
103
  const visitLocaleKeys = (value, visitor, pathParts = [])=>{
@@ -110,7 +117,7 @@ const checkPluralResources = (root, filePath, json, requiredSuffixes, pluralSuff
110
117
  visitLocaleKeys(json, (key, value, pathParts)=>{
111
118
  if ('string' != typeof value || !value.includes('{{count}}')) return;
112
119
  const suffixMatch = key.match(pluralSuffixPattern);
113
- if (!suffixMatch) throw new Error(`${relative} key ${pathParts.join('.')} contains {{count}} but is not plural-suffixed.`);
120
+ if (!suffixMatch) throw new SourceViolation(`${relative} key ${pathParts.join('.')} contains {{count}} but is not plural-suffixed.`);
114
121
  const [, base = '', suffix = ''] = suffixMatch;
115
122
  const parentPath = pathParts.slice(0, -1).join('.');
116
123
  const groupKey = `${parentPath}.${base}`;
@@ -118,7 +125,7 @@ const checkPluralResources = (root, filePath, json, requiredSuffixes, pluralSuff
118
125
  existing.add(suffix);
119
126
  groups.set(groupKey, existing);
120
127
  });
121
- for (const [group, suffixes] of groups)for (const suffix of requiredSuffixes)if (!suffixes.has(suffix)) throw new Error(`${relative} plural group ${group} is missing _${suffix}.`);
128
+ for (const [group, suffixes] of groups)for (const suffix of requiredSuffixes)if (!suffixes.has(suffix)) throw new SourceViolation(`${relative} plural group ${group} is missing _${suffix}.`);
122
129
  };
123
130
  const runRuntimeAndLocaleResourceChecks = (root, sourceRoots, locales, pluralCategories)=>{
124
131
  if (0 === locales.length) return;
@@ -140,11 +147,7 @@ const runRuntimeAndLocaleResourceChecks = (root, sourceRoots, locales, pluralCat
140
147
  checkPluralResources(root, filePath, JSON.parse(readText(filePath)), localeCategories.get(language) ?? [], pluralSuffixPattern);
141
148
  }
142
149
  };
143
- const runWorkspaceSourceCheck = ({ cwd = process.cwd(), sourceRoots = [
144
- 'apps',
145
- 'verticals',
146
- 'packages'
147
- ], locales = DEFAULT_LOCALES, pluralCategories } = {})=>{
150
+ const runWorkspaceSourceCheck = ({ cwd = process.cwd(), sourceRoots = DEFAULT_SOURCE_ROOTS, locales = DEFAULT_LOCALES, pluralCategories } = {})=>{
148
151
  const oxlintResult = (0, external_oxlint_cjs_namespaceObject.runOxlintRules)({
149
152
  cwd,
150
153
  targets: sourceRoots,
@@ -177,20 +180,90 @@ const runWorkspaceSourceCheck = ({ cwd = process.cwd(), sourceRoots = [
177
180
  try {
178
181
  runRuntimeAndLocaleResourceChecks(cwd, sourceRoots, locales, pluralCategories);
179
182
  } catch (error) {
180
- console.error(error instanceof Error ? error.message : 'UltraModern workspace source checks failed.');
181
- return 1;
183
+ console.error(error instanceof Error ? error.message : String(error));
184
+ return error instanceof SourceViolation ? 1 : 2;
182
185
  }
183
186
  console.log(WORKSPACE_SOURCE_SUCCESS);
184
187
  return 0;
185
188
  };
186
- const main = ()=>{
187
- process.exitCode = runWorkspaceSourceCheck();
189
+ const USAGE = `modern-i18n-check [--workspace-root <path>]
190
+ Runs the UltraModern i18n and boundary source checks for a workspace.
191
+ Workspace defaults to ULTRAMODERN_WORKSPACE_ROOT then cwd. Options come from
192
+ package.json "modernjs.i18nCheck": { sourceRoots, locales, pluralCategories }.
193
+ Exit codes: 0 valid, 1 source violation, 2 tool/configuration failure.`;
194
+ const isStringArray = (value)=>Array.isArray(value) && value.every((entry)=>'string' == typeof entry && entry.length > 0);
195
+ const isLocaleArray = (value)=>{
196
+ if (!isStringArray(value)) return false;
197
+ try {
198
+ Intl.getCanonicalLocales(value);
199
+ return true;
200
+ } catch {
201
+ return false;
202
+ }
203
+ };
204
+ const isRecord = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
205
+ const isWorkspaceDirectory = (root, entry)=>{
206
+ if (external_node_path_default().isAbsolute(entry)) return false;
207
+ const target = external_node_path_default().resolve(root, entry);
208
+ if (!external_node_fs_default().existsSync(target) || !external_node_fs_default().statSync(target).isDirectory()) return false;
209
+ const relative = external_node_path_default().relative(external_node_fs_default().realpathSync(root), external_node_fs_default().realpathSync(target));
210
+ return '' !== relative && '..' !== relative && !relative.startsWith(`..${external_node_path_default().sep}`) && !external_node_path_default().isAbsolute(relative);
211
+ };
212
+ const CLDR_PLURAL_CATEGORIES = new Set([
213
+ 'zero',
214
+ 'one',
215
+ 'two',
216
+ 'few',
217
+ 'many',
218
+ 'other'
219
+ ]);
220
+ const isPluralCategories = (value)=>isRecord(value) && Object.values(value).every((categories)=>isStringArray(categories) && categories.includes('other') && categories.every((category)=>CLDR_PLURAL_CATEGORIES.has(category)));
221
+ const readWorkspaceCheckOptions = (root)=>{
222
+ const manifestPath = external_node_path_default().join(root, 'package.json');
223
+ const modernjs = JSON.parse(readText(manifestPath)).modernjs;
224
+ const config = isRecord(modernjs) && Object.hasOwn(modernjs, 'i18nCheck') ? modernjs.i18nCheck : {};
225
+ const invalid = (field, expected)=>new Error(`${manifestPath} "modernjs.i18nCheck${field}" must be ${expected}.`);
226
+ if (!isRecord(config)) throw invalid('', 'an object');
227
+ const { locales, pluralCategories } = config;
228
+ if (void 0 !== locales && !isLocaleArray(locales)) throw invalid('.locales', 'an array of BCP 47 locale codes');
229
+ if (void 0 !== pluralCategories && !isPluralCategories(pluralCategories)) throw invalid('.pluralCategories', 'an object of locale -> CLDR plural categories including "other"');
230
+ const sourceRoots = Object.hasOwn(config, 'sourceRoots') ? config.sourceRoots : DEFAULT_SOURCE_ROOTS.filter((entry)=>isWorkspaceDirectory(root, entry));
231
+ if (!isStringArray(sourceRoots) || 0 === sourceRoots.length || !sourceRoots.every((entry)=>isWorkspaceDirectory(root, entry))) throw invalid('.sourceRoots', `a non-empty array of existing workspace-relative directories (default: whichever of ${DEFAULT_SOURCE_ROOTS.join(', ')} exist)`);
232
+ return {
233
+ cwd: root,
234
+ sourceRoots,
235
+ locales,
236
+ pluralCategories
237
+ };
238
+ };
239
+ const runWorkspaceSourceCheckCli = (args = process.argv.slice(2))=>{
240
+ let workspaceRoot = process.env.ULTRAMODERN_WORKSPACE_ROOT ?? process.cwd();
241
+ for(let index = 0; index < args.length; index += 1){
242
+ const argument = args[index];
243
+ if ('--help' === argument || '-h' === argument) {
244
+ console.log(USAGE);
245
+ return 0;
246
+ }
247
+ const value = args[index + 1];
248
+ if ('--workspace-root' !== argument || !value || value.startsWith('--')) {
249
+ console.error(`Invalid argument: ${argument ?? ''}. Use --help.`);
250
+ return 2;
251
+ }
252
+ workspaceRoot = value;
253
+ index += 1;
254
+ }
255
+ try {
256
+ return runWorkspaceSourceCheck(readWorkspaceCheckOptions(external_node_path_default().resolve(workspaceRoot)));
257
+ } catch (error) {
258
+ console.error(error instanceof Error ? error.message : String(error));
259
+ return 2;
260
+ }
188
261
  };
189
- exports.main = __webpack_exports__.main;
190
262
  exports.runWorkspaceSourceCheck = __webpack_exports__.runWorkspaceSourceCheck;
263
+ exports.runWorkspaceSourceCheckCli = __webpack_exports__.runWorkspaceSourceCheckCli;
191
264
  for(var __rspack_i in __webpack_exports__)if (-1 === [
192
- "main",
193
- "runWorkspaceSourceCheck"
265
+ "runWorkspaceSourceCheck",
266
+ "runWorkspaceSourceCheckCli"
194
267
  ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
195
268
  Object.defineProperty(exports, '__esModule', {
196
269
  value: true