@onexapis/cli 1.1.10 → 1.1.12

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.mjs CHANGED
@@ -110,8 +110,37 @@ async function resolveNodeModulesFile(startDir, relativePath) {
110
110
  }
111
111
  return null;
112
112
  }
113
+ async function scanImportsFromPackage(sourceDir, packageName) {
114
+ const result = {};
115
+ const escapedPkg = packageName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
116
+ const importRegex = new RegExp(
117
+ `(?:import|export)\\s+(?:type\\s+)?\\{([^}]+)\\}\\s+from\\s+["']${escapedPkg}(/[\\w./-]+)?["']`,
118
+ "g"
119
+ );
120
+ const sourceFiles = await glob("**/*.{ts,tsx}", {
121
+ cwd: sourceDir,
122
+ ignore: ["node_modules/**", "dist/**"]
123
+ });
124
+ for (const file of sourceFiles) {
125
+ try {
126
+ const content = await fs6.readFile(path7.join(sourceDir, file), "utf-8");
127
+ for (const match of content.matchAll(importRegex)) {
128
+ const subpath = match[2] ? match[2].slice(1) : "";
129
+ if (!result[subpath]) result[subpath] = /* @__PURE__ */ new Set();
130
+ for (const name of match[1].split(",")) {
131
+ let original = name.trim().split(/\s+as\s+/)[0].trim();
132
+ if (original.startsWith("type ")) continue;
133
+ if (original) result[subpath].add(original);
134
+ }
135
+ }
136
+ } catch {
137
+ }
138
+ }
139
+ return result;
140
+ }
113
141
  function createCoreGlobalPlugin(themePath) {
114
142
  const exportsBySubpath = {};
143
+ let scanPromise = null;
115
144
  return {
116
145
  name: "core-global",
117
146
  setup(build2) {
@@ -120,6 +149,16 @@ function createCoreGlobalPlugin(themePath) {
120
149
  namespace: "core-global"
121
150
  }));
122
151
  build2.onLoad({ filter: /.*/, namespace: "core-global" }, async (args) => {
152
+ if (!scanPromise) {
153
+ scanPromise = scanImportsFromPackage(themePath, "@onexapis/core").then((scanned) => {
154
+ for (const [subpath2, names] of Object.entries(scanned)) {
155
+ const cacheKey2 = subpath2 || "__root__";
156
+ exportsBySubpath[cacheKey2] = [...names];
157
+ }
158
+ }).catch(() => {
159
+ });
160
+ }
161
+ await scanPromise;
123
162
  const match = args.path.match(/^@onexapis\/core(\/(.+))?$/);
124
163
  const subpath = match?.[2] || "";
125
164
  const moduleAccess = subpath ? `['${subpath}']` : "";
@@ -183,6 +222,160 @@ ${namedExportLines}
183
222
  }
184
223
  };
185
224
  }
225
+ function createThemeDepsStubPlugin(themePath) {
226
+ return {
227
+ name: "theme-deps-stub",
228
+ setup(build2) {
229
+ const tryResolveOrStub = (filter, namespace) => {
230
+ build2.onResolve({ filter }, async (args) => {
231
+ if (args.pluginData?.skipStub) return void 0;
232
+ try {
233
+ const result = await build2.resolve(args.path, {
234
+ kind: args.kind,
235
+ resolveDir: args.resolveDir || themePath,
236
+ importer: args.importer,
237
+ namespace: "file",
238
+ pluginData: { skipStub: true }
239
+ });
240
+ if (!result.errors.length) return result;
241
+ } catch {
242
+ }
243
+ return { path: args.path, namespace };
244
+ });
245
+ };
246
+ tryResolveOrStub(/^next\//, "next-stub");
247
+ build2.onLoad({ filter: /.*/, namespace: "next-stub" }, (args) => {
248
+ const stubs = {
249
+ "next/image": `
250
+ import React from 'react';
251
+ const Image = (props) => {
252
+ const { src, alt, width, height, fill, priority, ...rest } = props;
253
+ const imgSrc = typeof src === 'object' ? src.src : src;
254
+ return React.createElement('img', { src: imgSrc, alt, width: fill ? undefined : width, height: fill ? undefined : height, loading: priority ? 'eager' : 'lazy', ...rest });
255
+ };
256
+ export default Image;
257
+ `,
258
+ "next/link": `
259
+ import React from 'react';
260
+ const Link = ({ href, children, ...rest }) => React.createElement('a', { href, ...rest }, children);
261
+ export default Link;
262
+ `,
263
+ "next/navigation": `
264
+ 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(){} }; }
265
+ export function usePathname() { return window.location.pathname; }
266
+ export function useSearchParams() { return new URLSearchParams(window.location.search); }
267
+ export function useParams() { return {}; }
268
+ export function redirect(url) { window.location.href = url; }
269
+ export function notFound() { throw new Error('Not Found'); }
270
+ `,
271
+ "next/headers": `
272
+ export function cookies() { return { get(){}, getAll(){ return []; }, set(){}, delete(){}, has(){ return false; } }; }
273
+ export function headers() { return new Headers(); }
274
+ `
275
+ };
276
+ return {
277
+ contents: stubs[args.path] || "export default {};",
278
+ loader: "jsx",
279
+ resolveDir: themePath
280
+ };
281
+ });
282
+ const lucideImports = /* @__PURE__ */ new Set();
283
+ let lucideThemeScanned = false;
284
+ tryResolveOrStub(/^lucide-react/, "lucide-stub");
285
+ build2.onLoad({ filter: /.*/, namespace: "lucide-stub" }, async () => {
286
+ if (!lucideThemeScanned) {
287
+ lucideThemeScanned = true;
288
+ try {
289
+ const scanned = await scanImportsFromPackage(themePath, "lucide-react");
290
+ for (const names of Object.values(scanned)) {
291
+ for (const name of names) lucideImports.add(name);
292
+ }
293
+ } catch {
294
+ }
295
+ }
296
+ const iconNames = [...lucideImports];
297
+ const exports$1 = iconNames.map((n) => `icon as ${n}`).join(", ");
298
+ return {
299
+ contents: `
300
+ const icon = (props) => null;
301
+ export { ${exports$1} };
302
+ export default new Proxy({}, { get: (_, name) => name === '__esModule' ? true : icon });
303
+ `.trim(),
304
+ loader: "jsx"
305
+ };
306
+ });
307
+ tryResolveOrStub(/^framer-motion/, "motion-stub");
308
+ build2.onLoad({ filter: /.*/, namespace: "motion-stub" }, () => ({
309
+ contents: `
310
+ import React from 'react';
311
+ const handler = { get: (_, name) => {
312
+ if (name === '__esModule') return true;
313
+ return React.forwardRef((props, ref) => React.createElement(name, { ...props, ref }));
314
+ }};
315
+ export const motion = new Proxy({}, handler);
316
+ export const AnimatePresence = ({ children }) => children || null;
317
+ export function useInView() { return true; }
318
+ export default { motion, AnimatePresence, useInView };
319
+ `.trim(),
320
+ loader: "jsx",
321
+ resolveDir: themePath
322
+ }));
323
+ tryResolveOrStub(/^sonner$/, "sonner-stub");
324
+ build2.onLoad({ filter: /.*/, namespace: "sonner-stub" }, () => ({
325
+ contents: `
326
+ export const toast = new Proxy(() => {}, { get: () => () => {} });
327
+ export const Toaster = () => null;
328
+ export default { toast, Toaster };
329
+ `.trim(),
330
+ loader: "jsx"
331
+ }));
332
+ tryResolveOrStub(/^react-hook-form$/, "rhf-stub");
333
+ build2.onLoad({ filter: /.*/, namespace: "rhf-stub" }, () => ({
334
+ contents: `
335
+ export function useForm() {
336
+ return {
337
+ register: () => ({}),
338
+ handleSubmit: (fn) => (e) => { e?.preventDefault?.(); fn({}); },
339
+ formState: { errors: {}, isSubmitting: false, isValid: true },
340
+ watch: () => undefined,
341
+ setValue: () => {},
342
+ reset: () => {},
343
+ control: {},
344
+ };
345
+ }
346
+ export function useController() { return { field: {}, fieldState: {} }; }
347
+ export function useFormContext() { return useForm(); }
348
+ `.trim(),
349
+ loader: "js"
350
+ }));
351
+ tryResolveOrStub(/^@hookform\/resolvers/, "hookform-resolvers-stub");
352
+ build2.onLoad({ filter: /.*/, namespace: "hookform-resolvers-stub" }, () => ({
353
+ contents: `export function zodResolver() { return () => ({ values: {}, errors: {} }); }`,
354
+ loader: "js"
355
+ }));
356
+ tryResolveOrStub(/^next-intl$/, "next-intl-stub");
357
+ build2.onLoad({ filter: /.*/, namespace: "next-intl-stub" }, () => ({
358
+ contents: `
359
+ export function useTranslations(ns) {
360
+ return (key) => ns ? ns + '.' + key : key;
361
+ }
362
+ export function useLocale() { return 'en'; }
363
+ export function useMessages() { return {}; }
364
+ `.trim(),
365
+ loader: "js"
366
+ }));
367
+ tryResolveOrStub(/^zod$/, "zod-stub");
368
+ build2.onLoad({ filter: /.*/, namespace: "zod-stub" }, () => ({
369
+ contents: `
370
+ 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 });
371
+ export const z = { string: schema, number: schema, boolean: schema, object: (s) => ({ ...schema(), shape: s }), array: schema, enum: schema, union: schema, literal: schema, infer: undefined };
372
+ export default z;
373
+ `.trim(),
374
+ loader: "js"
375
+ }));
376
+ }
377
+ };
378
+ }
186
379
  async function generateThemeData(themePath, outputDir, themeId) {
187
380
  const { createJiti } = await import('jiti');
188
381
  const jiti = createJiti(import.meta.url);
@@ -417,7 +610,7 @@ async function compileStandaloneTheme(themePath, themeName) {
417
610
  banner: {
418
611
  js: '"use client";'
419
612
  },
420
- plugins: [reactGlobalPlugin, createCoreGlobalPlugin(themePath)],
613
+ plugins: [reactGlobalPlugin, createCoreGlobalPlugin(themePath), createThemeDepsStubPlugin(themePath)],
421
614
  external: [],
422
615
  alias: {
423
616
  events: "events/",
@@ -499,7 +692,7 @@ async function compileStandaloneThemeDev(themePath, themeName) {
499
692
  banner: {
500
693
  js: '"use client";'
501
694
  },
502
- plugins: [reactGlobalPlugin, createCoreGlobalPlugin(themePath)],
695
+ plugins: [reactGlobalPlugin, createCoreGlobalPlugin(themePath), createThemeDepsStubPlugin(themePath)],
503
696
  external: [],
504
697
  alias: {
505
698
  events: "events/",
@@ -610,6 +803,8 @@ ${locations.join("\n")}`
610
803
  loader: "js"
611
804
  };
612
805
  });
806
+ const lucideIconNames = /* @__PURE__ */ new Set();
807
+ let lucideScanned = false;
613
808
  build2.onResolve({ filter: /^lucide-react/ }, async (args) => {
614
809
  if (args.pluginData?.skipStub) return void 0;
615
810
  try {
@@ -625,23 +820,93 @@ ${locations.join("\n")}`
625
820
  }
626
821
  return { path: args.path, namespace: "lucide-stub" };
627
822
  });
628
- build2.onLoad({ filter: /.*/, namespace: "lucide-stub" }, () => ({
629
- // Provide all icon names used by @onexapis/core as no-op SVG stub components
630
- contents: `
823
+ build2.onLoad({ filter: /.*/, namespace: "lucide-stub" }, async () => {
824
+ if (!lucideScanned) {
825
+ lucideScanned = true;
826
+ const coreSrcCandidates = [
827
+ path7.join(themePath, "node_modules", "@onexapis", "core", "src"),
828
+ path7.join(themePath, "..", "..", "packages", "core", "src"),
829
+ // monorepo sibling
830
+ path7.join(__dirname, "..", "..", "..", "..", "packages", "core", "src")
831
+ // from CLI src
832
+ ];
833
+ let coreSourceDir = null;
834
+ for (const candidate of coreSrcCandidates) {
835
+ try {
836
+ await fs6.access(candidate);
837
+ coreSourceDir = candidate;
838
+ break;
839
+ } catch {
840
+ }
841
+ }
842
+ if (coreSourceDir) {
843
+ try {
844
+ const scanned = await scanImportsFromPackage(coreSourceDir, "lucide-react");
845
+ for (const names of Object.values(scanned)) {
846
+ for (const name of names) lucideIconNames.add(name);
847
+ }
848
+ } catch {
849
+ }
850
+ }
851
+ try {
852
+ const scanned = await scanImportsFromPackage(themePath, "lucide-react");
853
+ for (const names of Object.values(scanned)) {
854
+ for (const name of names) lucideIconNames.add(name);
855
+ }
856
+ } catch {
857
+ }
858
+ }
859
+ if (lucideIconNames.size === 0) {
860
+ const fallbackIcons = [
861
+ "Check",
862
+ "ChevronDown",
863
+ "XCircle",
864
+ "AlertTriangle",
865
+ "CheckCircle",
866
+ "Info",
867
+ "X",
868
+ "XIcon",
869
+ "CircleIcon",
870
+ "Star",
871
+ "ShoppingCart",
872
+ "ChevronRight",
873
+ "ChevronLeft",
874
+ "ChevronUp",
875
+ "Search",
876
+ "Menu",
877
+ "Heart",
878
+ "User",
879
+ "Trash2",
880
+ "Plus",
881
+ "Minus",
882
+ "Eye",
883
+ "EyeOff",
884
+ "ArrowRight",
885
+ "ArrowLeft",
886
+ "ExternalLink",
887
+ "Mail",
888
+ "Phone",
889
+ "MapPin",
890
+ "Calendar",
891
+ "Clock",
892
+ "Facebook",
893
+ "Twitter",
894
+ "Instagram",
895
+ "Linkedin",
896
+ "Github"
897
+ ];
898
+ for (const icon of fallbackIcons) lucideIconNames.add(icon);
899
+ }
900
+ const iconExports = [...lucideIconNames].map((n) => `icon as ${n}`).join(", ");
901
+ return {
902
+ contents: `
631
903
  const icon = (props) => null;
632
- export { icon as Check, icon as ChevronDown, icon as XCircle, icon as AlertTriangle };
633
- export { icon as CheckCircle, icon as Info, icon as X, icon as XIcon };
634
- export { icon as CircleIcon, icon as Star, icon as ShoppingCart };
635
- export { icon as ChevronRight, icon as ChevronLeft, icon as ChevronUp };
636
- export { icon as Search, icon as Menu, icon as Heart, icon as User };
637
- export { icon as Trash2, icon as Plus, icon as Minus, icon as Eye, icon as EyeOff };
638
- export { icon as ArrowRight, icon as ArrowLeft, icon as ExternalLink, icon as Mail };
639
- export { icon as Phone, icon as MapPin, icon as Calendar, icon as Clock };
640
- export { icon as Facebook, icon as Twitter, icon as Instagram, icon as Linkedin, icon as Github };
904
+ export { ${iconExports} };
641
905
  export default new Proxy({}, { get: (_, name) => name === '__esModule' ? true : icon });
642
- `.trim(),
643
- loader: "jsx"
644
- }));
906
+ `.trim(),
907
+ loader: "jsx"
908
+ };
909
+ });
645
910
  build2.onResolve({ filter: /^framer-motion/ }, async (args) => {
646
911
  if (args.pluginData?.skipStub) return void 0;
647
912
  try {