@onexapis/cli 1.1.9 → 1.1.11

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.
package/dist/index.js CHANGED
@@ -147,8 +147,37 @@ async function resolveNodeModulesFile(startDir, relativePath) {
147
147
  }
148
148
  return null;
149
149
  }
150
+ async function scanImportsFromPackage(sourceDir, packageName) {
151
+ const result = {};
152
+ const escapedPkg = packageName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
153
+ const importRegex = new RegExp(
154
+ `(?:import|export)\\s+(?:type\\s+)?\\{([^}]+)\\}\\s+from\\s+["']${escapedPkg}(/[\\w./-]+)?["']`,
155
+ "g"
156
+ );
157
+ const sourceFiles = await glob.glob("**/*.{ts,tsx}", {
158
+ cwd: sourceDir,
159
+ ignore: ["node_modules/**", "dist/**"]
160
+ });
161
+ for (const file of sourceFiles) {
162
+ try {
163
+ const content = await fs6__default.default.readFile(path7__default.default.join(sourceDir, file), "utf-8");
164
+ for (const match of content.matchAll(importRegex)) {
165
+ const subpath = match[2] ? match[2].slice(1) : "";
166
+ if (!result[subpath]) result[subpath] = /* @__PURE__ */ new Set();
167
+ for (const name of match[1].split(",")) {
168
+ let original = name.trim().split(/\s+as\s+/)[0].trim();
169
+ if (original.startsWith("type ")) continue;
170
+ if (original) result[subpath].add(original);
171
+ }
172
+ }
173
+ } catch {
174
+ }
175
+ }
176
+ return result;
177
+ }
150
178
  function createCoreGlobalPlugin(themePath) {
151
179
  const exportsBySubpath = {};
180
+ let scanPromise = null;
152
181
  return {
153
182
  name: "core-global",
154
183
  setup(build2) {
@@ -157,6 +186,16 @@ function createCoreGlobalPlugin(themePath) {
157
186
  namespace: "core-global"
158
187
  }));
159
188
  build2.onLoad({ filter: /.*/, namespace: "core-global" }, async (args) => {
189
+ if (!scanPromise) {
190
+ scanPromise = scanImportsFromPackage(themePath, "@onexapis/core").then((scanned) => {
191
+ for (const [subpath2, names] of Object.entries(scanned)) {
192
+ const cacheKey2 = subpath2 || "__root__";
193
+ exportsBySubpath[cacheKey2] = [...names];
194
+ }
195
+ }).catch(() => {
196
+ });
197
+ }
198
+ await scanPromise;
160
199
  const match = args.path.match(/^@onexapis\/core(\/(.+))?$/);
161
200
  const subpath = match?.[2] || "";
162
201
  const moduleAccess = subpath ? `['${subpath}']` : "";
@@ -220,6 +259,160 @@ ${namedExportLines}
220
259
  }
221
260
  };
222
261
  }
262
+ function createThemeDepsStubPlugin(themePath) {
263
+ return {
264
+ name: "theme-deps-stub",
265
+ setup(build2) {
266
+ const tryResolveOrStub = (filter, namespace) => {
267
+ build2.onResolve({ filter }, async (args) => {
268
+ if (args.pluginData?.skipStub) return void 0;
269
+ try {
270
+ const result = await build2.resolve(args.path, {
271
+ kind: args.kind,
272
+ resolveDir: args.resolveDir || themePath,
273
+ importer: args.importer,
274
+ namespace: "file",
275
+ pluginData: { skipStub: true }
276
+ });
277
+ if (!result.errors.length) return result;
278
+ } catch {
279
+ }
280
+ return { path: args.path, namespace };
281
+ });
282
+ };
283
+ tryResolveOrStub(/^next\//, "next-stub");
284
+ build2.onLoad({ filter: /.*/, namespace: "next-stub" }, (args) => {
285
+ const stubs = {
286
+ "next/image": `
287
+ import React from 'react';
288
+ const Image = (props) => {
289
+ const { src, alt, width, height, fill, priority, ...rest } = props;
290
+ const imgSrc = typeof src === 'object' ? src.src : src;
291
+ return React.createElement('img', { src: imgSrc, alt, width: fill ? undefined : width, height: fill ? undefined : height, loading: priority ? 'eager' : 'lazy', ...rest });
292
+ };
293
+ export default Image;
294
+ `,
295
+ "next/link": `
296
+ import React from 'react';
297
+ const Link = ({ href, children, ...rest }) => React.createElement('a', { href, ...rest }, children);
298
+ export default Link;
299
+ `,
300
+ "next/navigation": `
301
+ export function useRouter() { return { push(u){window.location.href=u}, replace(u){window.location.href=u}, back(){window.history.back()}, forward(){window.history.forward()}, refresh(){window.location.reload()}, prefetch(){} }; }
302
+ export function usePathname() { return window.location.pathname; }
303
+ export function useSearchParams() { return new URLSearchParams(window.location.search); }
304
+ export function useParams() { return {}; }
305
+ export function redirect(url) { window.location.href = url; }
306
+ export function notFound() { throw new Error('Not Found'); }
307
+ `,
308
+ "next/headers": `
309
+ export function cookies() { return { get(){}, getAll(){ return []; }, set(){}, delete(){}, has(){ return false; } }; }
310
+ export function headers() { return new Headers(); }
311
+ `
312
+ };
313
+ return {
314
+ contents: stubs[args.path] || "export default {};",
315
+ loader: "jsx",
316
+ resolveDir: themePath
317
+ };
318
+ });
319
+ const lucideImports = /* @__PURE__ */ new Set();
320
+ let lucideThemeScanned = false;
321
+ tryResolveOrStub(/^lucide-react/, "lucide-stub");
322
+ build2.onLoad({ filter: /.*/, namespace: "lucide-stub" }, async () => {
323
+ if (!lucideThemeScanned) {
324
+ lucideThemeScanned = true;
325
+ try {
326
+ const scanned = await scanImportsFromPackage(themePath, "lucide-react");
327
+ for (const names of Object.values(scanned)) {
328
+ for (const name of names) lucideImports.add(name);
329
+ }
330
+ } catch {
331
+ }
332
+ }
333
+ const iconNames = [...lucideImports];
334
+ const exports$1 = iconNames.map((n) => `icon as ${n}`).join(", ");
335
+ return {
336
+ contents: `
337
+ const icon = (props) => null;
338
+ export { ${exports$1} };
339
+ export default new Proxy({}, { get: (_, name) => name === '__esModule' ? true : icon });
340
+ `.trim(),
341
+ loader: "jsx"
342
+ };
343
+ });
344
+ tryResolveOrStub(/^framer-motion/, "motion-stub");
345
+ build2.onLoad({ filter: /.*/, namespace: "motion-stub" }, () => ({
346
+ contents: `
347
+ import React from 'react';
348
+ const handler = { get: (_, name) => {
349
+ if (name === '__esModule') return true;
350
+ return React.forwardRef((props, ref) => React.createElement(name, { ...props, ref }));
351
+ }};
352
+ export const motion = new Proxy({}, handler);
353
+ export const AnimatePresence = ({ children }) => children || null;
354
+ export function useInView() { return true; }
355
+ export default { motion, AnimatePresence, useInView };
356
+ `.trim(),
357
+ loader: "jsx",
358
+ resolveDir: themePath
359
+ }));
360
+ tryResolveOrStub(/^sonner$/, "sonner-stub");
361
+ build2.onLoad({ filter: /.*/, namespace: "sonner-stub" }, () => ({
362
+ contents: `
363
+ export const toast = new Proxy(() => {}, { get: () => () => {} });
364
+ export const Toaster = () => null;
365
+ export default { toast, Toaster };
366
+ `.trim(),
367
+ loader: "jsx"
368
+ }));
369
+ tryResolveOrStub(/^react-hook-form$/, "rhf-stub");
370
+ build2.onLoad({ filter: /.*/, namespace: "rhf-stub" }, () => ({
371
+ contents: `
372
+ export function useForm() {
373
+ return {
374
+ register: () => ({}),
375
+ handleSubmit: (fn) => (e) => { e?.preventDefault?.(); fn({}); },
376
+ formState: { errors: {}, isSubmitting: false, isValid: true },
377
+ watch: () => undefined,
378
+ setValue: () => {},
379
+ reset: () => {},
380
+ control: {},
381
+ };
382
+ }
383
+ export function useController() { return { field: {}, fieldState: {} }; }
384
+ export function useFormContext() { return useForm(); }
385
+ `.trim(),
386
+ loader: "js"
387
+ }));
388
+ tryResolveOrStub(/^@hookform\/resolvers/, "hookform-resolvers-stub");
389
+ build2.onLoad({ filter: /.*/, namespace: "hookform-resolvers-stub" }, () => ({
390
+ contents: `export function zodResolver() { return () => ({ values: {}, errors: {} }); }`,
391
+ loader: "js"
392
+ }));
393
+ tryResolveOrStub(/^next-intl$/, "next-intl-stub");
394
+ build2.onLoad({ filter: /.*/, namespace: "next-intl-stub" }, () => ({
395
+ contents: `
396
+ export function useTranslations(ns) {
397
+ return (key) => ns ? ns + '.' + key : key;
398
+ }
399
+ export function useLocale() { return 'en'; }
400
+ export function useMessages() { return {}; }
401
+ `.trim(),
402
+ loader: "js"
403
+ }));
404
+ tryResolveOrStub(/^zod$/, "zod-stub");
405
+ build2.onLoad({ filter: /.*/, namespace: "zod-stub" }, () => ({
406
+ contents: `
407
+ const schema = () => ({ parse: (v) => v, safeParse: (v) => ({ success: true, data: v }), optional: schema, min: schema, max: schema, email: schema, url: schema, regex: schema, refine: schema, transform: schema });
408
+ export const z = { string: schema, number: schema, boolean: schema, object: (s) => ({ ...schema(), shape: s }), array: schema, enum: schema, union: schema, literal: schema, infer: undefined };
409
+ export default z;
410
+ `.trim(),
411
+ loader: "js"
412
+ }));
413
+ }
414
+ };
415
+ }
223
416
  async function generateThemeData(themePath, outputDir, themeId) {
224
417
  const { createJiti } = await import('jiti');
225
418
  const jiti = createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)));
@@ -454,7 +647,7 @@ async function compileStandaloneTheme(themePath, themeName) {
454
647
  banner: {
455
648
  js: '"use client";'
456
649
  },
457
- plugins: [reactGlobalPlugin, createCoreGlobalPlugin(themePath)],
650
+ plugins: [reactGlobalPlugin, createCoreGlobalPlugin(themePath), createThemeDepsStubPlugin(themePath)],
458
651
  external: [],
459
652
  alias: {
460
653
  events: "events/",
@@ -536,7 +729,7 @@ async function compileStandaloneThemeDev(themePath, themeName) {
536
729
  banner: {
537
730
  js: '"use client";'
538
731
  },
539
- plugins: [reactGlobalPlugin, createCoreGlobalPlugin(themePath)],
732
+ plugins: [reactGlobalPlugin, createCoreGlobalPlugin(themePath), createThemeDepsStubPlugin(themePath)],
540
733
  external: [],
541
734
  alias: {
542
735
  events: "events/",
@@ -647,6 +840,8 @@ ${locations.join("\n")}`
647
840
  loader: "js"
648
841
  };
649
842
  });
843
+ const lucideIconNames = /* @__PURE__ */ new Set();
844
+ let lucideScanned = false;
650
845
  build2.onResolve({ filter: /^lucide-react/ }, async (args) => {
651
846
  if (args.pluginData?.skipStub) return void 0;
652
847
  try {
@@ -662,23 +857,93 @@ ${locations.join("\n")}`
662
857
  }
663
858
  return { path: args.path, namespace: "lucide-stub" };
664
859
  });
665
- build2.onLoad({ filter: /.*/, namespace: "lucide-stub" }, () => ({
666
- // Provide all icon names used by @onexapis/core as no-op SVG stub components
667
- contents: `
860
+ build2.onLoad({ filter: /.*/, namespace: "lucide-stub" }, async () => {
861
+ if (!lucideScanned) {
862
+ lucideScanned = true;
863
+ const coreSrcCandidates = [
864
+ path7__default.default.join(themePath, "node_modules", "@onexapis", "core", "src"),
865
+ path7__default.default.join(themePath, "..", "..", "packages", "core", "src"),
866
+ // monorepo sibling
867
+ path7__default.default.join(__dirname, "..", "..", "..", "..", "packages", "core", "src")
868
+ // from CLI src
869
+ ];
870
+ let coreSourceDir = null;
871
+ for (const candidate of coreSrcCandidates) {
872
+ try {
873
+ await fs6__default.default.access(candidate);
874
+ coreSourceDir = candidate;
875
+ break;
876
+ } catch {
877
+ }
878
+ }
879
+ if (coreSourceDir) {
880
+ try {
881
+ const scanned = await scanImportsFromPackage(coreSourceDir, "lucide-react");
882
+ for (const names of Object.values(scanned)) {
883
+ for (const name of names) lucideIconNames.add(name);
884
+ }
885
+ } catch {
886
+ }
887
+ }
888
+ try {
889
+ const scanned = await scanImportsFromPackage(themePath, "lucide-react");
890
+ for (const names of Object.values(scanned)) {
891
+ for (const name of names) lucideIconNames.add(name);
892
+ }
893
+ } catch {
894
+ }
895
+ }
896
+ if (lucideIconNames.size === 0) {
897
+ const fallbackIcons = [
898
+ "Check",
899
+ "ChevronDown",
900
+ "XCircle",
901
+ "AlertTriangle",
902
+ "CheckCircle",
903
+ "Info",
904
+ "X",
905
+ "XIcon",
906
+ "CircleIcon",
907
+ "Star",
908
+ "ShoppingCart",
909
+ "ChevronRight",
910
+ "ChevronLeft",
911
+ "ChevronUp",
912
+ "Search",
913
+ "Menu",
914
+ "Heart",
915
+ "User",
916
+ "Trash2",
917
+ "Plus",
918
+ "Minus",
919
+ "Eye",
920
+ "EyeOff",
921
+ "ArrowRight",
922
+ "ArrowLeft",
923
+ "ExternalLink",
924
+ "Mail",
925
+ "Phone",
926
+ "MapPin",
927
+ "Calendar",
928
+ "Clock",
929
+ "Facebook",
930
+ "Twitter",
931
+ "Instagram",
932
+ "Linkedin",
933
+ "Github"
934
+ ];
935
+ for (const icon of fallbackIcons) lucideIconNames.add(icon);
936
+ }
937
+ const iconExports = [...lucideIconNames].map((n) => `icon as ${n}`).join(", ");
938
+ return {
939
+ contents: `
668
940
  const icon = (props) => null;
669
- export { icon as Check, icon as ChevronDown, icon as XCircle, icon as AlertTriangle };
670
- export { icon as CheckCircle, icon as Info, icon as X, icon as XIcon };
671
- export { icon as CircleIcon, icon as Star, icon as ShoppingCart };
672
- export { icon as ChevronRight, icon as ChevronLeft, icon as ChevronUp };
673
- export { icon as Search, icon as Menu, icon as Heart, icon as User };
674
- export { icon as Trash2, icon as Plus, icon as Minus, icon as Eye, icon as EyeOff };
675
- export { icon as ArrowRight, icon as ArrowLeft, icon as ExternalLink, icon as Mail };
676
- export { icon as Phone, icon as MapPin, icon as Calendar, icon as Clock };
677
- export { icon as Facebook, icon as Twitter, icon as Instagram, icon as Linkedin, icon as Github };
941
+ export { ${iconExports} };
678
942
  export default new Proxy({}, { get: (_, name) => name === '__esModule' ? true : icon });
679
- `.trim(),
680
- loader: "jsx"
681
- }));
943
+ `.trim(),
944
+ loader: "jsx"
945
+ };
946
+ });
682
947
  build2.onResolve({ filter: /^framer-motion/ }, async (args) => {
683
948
  if (args.pluginData?.skipStub) return void 0;
684
949
  try {