@_linked/cli 1.4.2 → 1.4.4

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 (85) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/defaults/app-static/src/index-static.tsx +22 -0
  3. package/defaults/app-with-backend/src/App.module.css +32 -0
  4. package/defaults/app-with-backend/src/App.tsx +30 -0
  5. package/defaults/app-with-backend/src/backend.ts +9 -0
  6. package/defaults/app-with-backend/src/components/Error.module.css +9 -0
  7. package/defaults/app-with-backend/src/components/Error.tsx +11 -0
  8. package/defaults/app-with-backend/src/components/PersonOverview.module.css +100 -0
  9. package/defaults/app-with-backend/src/components/PersonOverview.tsx +97 -0
  10. package/defaults/app-with-backend/src/components/PersonOverviewContext.tsx +12 -0
  11. package/defaults/app-with-backend/src/components/PersonPreview.module.css +97 -0
  12. package/defaults/app-with-backend/src/components/PersonPreview.tsx +107 -0
  13. package/defaults/app-with-backend/src/components/Spinner.module.css +17 -0
  14. package/defaults/app-with-backend/src/components/Spinner.tsx +12 -0
  15. package/defaults/app-with-backend/src/index.tsx +34 -0
  16. package/defaults/app-with-backend/src/layout/DefaultLayout.module.css +15 -0
  17. package/defaults/app-with-backend/src/layout/DefaultLayout.tsx +12 -0
  18. package/defaults/app-with-backend/src/layout/Header.module.css +46 -0
  19. package/defaults/app-with-backend/src/layout/Header.tsx +27 -0
  20. package/defaults/app-with-backend/src/linked.frontend.datasets.json +12 -0
  21. package/defaults/app-with-backend/src/linked.frontend.storage.ts +36 -0
  22. package/defaults/app-with-backend/src/package.ts +12 -0
  23. package/defaults/app-with-backend/src/pages/Home.module.css +23 -0
  24. package/defaults/app-with-backend/src/pages/Home.tsx +14 -0
  25. package/defaults/app-with-backend/src/pages/Page1.module.css +18 -0
  26. package/defaults/app-with-backend/src/pages/Page1.tsx +16 -0
  27. package/defaults/app-with-backend/src/pages/PageNotFound.module.css +19 -0
  28. package/defaults/app-with-backend/src/pages/PageNotFound.tsx +14 -0
  29. package/defaults/app-with-backend/src/pages/Signin.module.css +20 -0
  30. package/defaults/app-with-backend/src/pages/Signin.tsx +16 -0
  31. package/defaults/app-with-backend/src/routes.tsx +85 -0
  32. package/defaults/app-with-backend/src/theme.css +91 -0
  33. package/defaults/app-with-backend/src/types.ts +14 -0
  34. package/defaults/app-with-backend/src/utils/lazyWithPreload.tsx +42 -0
  35. package/defaults/app-with-backend/src/utils/preloadRoutes.ts +38 -0
  36. package/defaults/app-with-backend/tsconfig.json +24 -0
  37. package/defaults/package/package.json +7 -3
  38. package/lib/cjs/cli-deprecated.d.ts +2 -0
  39. package/lib/cjs/cli-methods.d.ts +67 -0
  40. package/lib/cjs/cli.d.ts +3 -0
  41. package/lib/cjs/commands/build-package.d.ts +9 -0
  42. package/lib/cjs/commands/safe-yarn.d.ts +10 -0
  43. package/lib/cjs/commands/setup-publish.d.ts +23 -0
  44. package/lib/cjs/config-webpack-app.d.ts +112 -0
  45. package/lib/cjs/config-webpack.d.ts +55 -0
  46. package/lib/cjs/defineConfig.d.ts +8 -0
  47. package/lib/cjs/index.d.ts +12 -0
  48. package/lib/cjs/interfaces.d.ts +131 -0
  49. package/lib/cjs/loaders/register-css-only.d.ts +2 -0
  50. package/lib/cjs/loaders/register.d.ts +2 -0
  51. package/lib/cjs/metadata.d.ts +1 -0
  52. package/lib/cjs/package.json +1 -1
  53. package/lib/cjs/plugins/check-imports.d.ts +2 -0
  54. package/lib/cjs/plugins/declaration-plugin.d.ts +15 -0
  55. package/lib/cjs/plugins/externalise-modules.d.ts +7 -0
  56. package/lib/cjs/plugins/lincd-tailwind-sources.d.ts +3 -0
  57. package/lib/cjs/plugins/watch-run.d.ts +5 -0
  58. package/lib/cjs/tailwind.config.d.ts +8 -0
  59. package/lib/cjs/utils.d.ts +77 -0
  60. package/lib/esm/cli-deprecated.d.ts +2 -0
  61. package/lib/esm/cli-methods.d.ts +67 -0
  62. package/lib/esm/cli.d.ts +3 -0
  63. package/lib/esm/commands/build-package.d.ts +9 -0
  64. package/lib/esm/commands/safe-yarn.d.ts +10 -0
  65. package/lib/esm/commands/setup-publish.d.ts +23 -0
  66. package/lib/esm/config-webpack-app.d.ts +112 -0
  67. package/lib/esm/config-webpack.d.ts +55 -0
  68. package/lib/esm/defineConfig.d.ts +8 -0
  69. package/lib/esm/index.d.ts +12 -0
  70. package/lib/esm/interfaces.d.ts +131 -0
  71. package/lib/esm/launch.d.ts +3 -0
  72. package/lib/esm/loaders/register-css-only.d.ts +2 -0
  73. package/lib/esm/loaders/register.d.ts +2 -0
  74. package/lib/esm/metadata.d.ts +1 -0
  75. package/lib/esm/package.json +1 -1
  76. package/lib/esm/plugins/check-imports.d.ts +2 -0
  77. package/lib/esm/plugins/declaration-plugin.d.ts +15 -0
  78. package/lib/esm/plugins/externalise-modules.d.ts +7 -0
  79. package/lib/esm/plugins/lincd-tailwind-sources.d.ts +3 -0
  80. package/lib/esm/plugins/watch-run.d.ts +5 -0
  81. package/lib/esm/tailwind.config.d.ts +8 -0
  82. package/lib/esm/utils.d.ts +77 -0
  83. package/package.json +1 -1
  84. package/tests/e2e/create-app.spec.ts +157 -0
  85. package/tests/unit/safeYarn.test.ts +55 -0
@@ -0,0 +1,46 @@
1
+ @import '@_linked/css/package.css';
2
+
3
+ .header {
4
+ display: flex;
5
+ align-items: center;
6
+ justify-content: space-between;
7
+ gap: --spacing(4);
8
+ padding: --spacing(4) 0 --spacing(6);
9
+ flex-wrap: wrap;
10
+ }
11
+
12
+ .header h1 {
13
+ color: var(--color-primary-700);
14
+ margin: 0;
15
+ }
16
+
17
+ .menu {
18
+ display: flex;
19
+ gap: --spacing(1);
20
+ flex-wrap: wrap;
21
+ }
22
+
23
+ .menu > a {
24
+ font-weight: 500;
25
+ font-size: 0.95rem;
26
+ color: var(--color-gray-700);
27
+ padding: --spacing(2) --spacing(3);
28
+ border-radius: var(--radius-element);
29
+ text-decoration: none;
30
+ transition: background 120ms ease, color 120ms ease;
31
+ }
32
+
33
+ .menu > a:hover {
34
+ background: var(--color-gray-100);
35
+ color: var(--color-gray-900);
36
+ }
37
+
38
+ .menu > a.active {
39
+ background: var(--color-primary-500);
40
+ color: var(--color-white);
41
+ }
42
+
43
+ .menu > a.active:hover {
44
+ background: var(--color-primary-600);
45
+ color: var(--color-white);
46
+ }
@@ -0,0 +1,27 @@
1
+ import { Link, useLocation } from 'react-router-dom';
2
+ import React from 'react';
3
+ import style from './Header.module.css';
4
+ import { ROUTES } from '../routes';
5
+
6
+ export function Header() {
7
+ let path = useLocation();
8
+ return (
9
+ <header className={style.header}>
10
+ <h1>${name}</h1>
11
+ <nav className={style.menu}>
12
+ {Object.keys(ROUTES).map((key) => {
13
+ if (ROUTES[key].excludeFromMenu) return null;
14
+ return (
15
+ <Link
16
+ key={key}
17
+ to={ROUTES[key].path}
18
+ className={ROUTES[key].path === path.pathname ? style.active : ''}
19
+ >
20
+ {ROUTES[key].label || key}
21
+ </Link>
22
+ );
23
+ })}
24
+ </nav>
25
+ </header>
26
+ );
27
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "_note": "Frontend datasets config. Public values only — this file ships in the browser bundle. Each entry: `store` (npm import path to the IDataset class) + `config` (a single JSON object passed verbatim to `new StoreClass(config)`). The default BackendAPIStore entry proxies queries to this app's own backend; swap in a different store class (e.g. @_linked/fuseki/shapes/FusekiStore) to talk directly to a public SPARQL endpoint.",
3
+
4
+ "datasets": {
5
+ "appData": {
6
+ "store": "@_linked/server/shapes/quadstores/BackendAPIStore",
7
+ "config": {
8
+ "name": "appData"
9
+ }
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,36 @@
1
+ // Frontend storage for ${name}.
2
+ //
3
+ // Reads linked.frontend.datasets.json and instantiates one store per
4
+ // alias. Webpack can't bundle dynamic imports of arbitrary npm paths,
5
+ // so on the frontend we import each store class explicitly and
6
+ // construct it per-alias here.
7
+ //
8
+ // Aliases on this side are independent from backend aliases — the
9
+ // framework re-routes by shape on each side.
10
+ import datasetsConfig from './linked.frontend.datasets.json' assert { type: 'json' };
11
+ import { parseDatasetsConfig } from '@_linked/core/utils/parseDatasetsConfig';
12
+ import { LinkedFileStorage } from '@_linked/core/utils/LinkedFileStorage';
13
+ import { LinkedStorage } from '@_linked/core/utils/LinkedStorage';
14
+ import { BackendAPIStore } from '@_linked/server/shapes/quadstores/BackendAPIStore';
15
+ import { getAccessUrlLocalFileStore } from '@_linked/server/utils/accessUrl';
16
+ // Direct-to-Fuseki on the frontend? Uncomment:
17
+ // import { FusekiStore } from '@_linked/fuseki/shapes/FusekiStore';
18
+
19
+ // Parse JSON; pass empty env since the browser has no runtime process.env.
20
+ // Per-env values for the frontend should be hardcoded `process.env.X`
21
+ // references (webpack inlines them at build time).
22
+ const config = parseDatasetsConfig(datasetsConfig, {});
23
+
24
+ // One store per alias. Each line imports the class above and constructs it
25
+ // with the alias's `config`. Adding a new alias = add an import + a line.
26
+ const appData = new BackendAPIStore(config.datasets.appData.config);
27
+ // Direct-to-Fuseki alternative (no creds in browser):
28
+ // const appData = new FusekiStore(config.datasets.appData.config);
29
+
30
+ // Shape → alias. For multi-alias setups add per-shape pins, e.g.:
31
+ // import { PageView } from './shapes/PageView';
32
+ // LinkedStorage.setDatasetForShapes(analytics, [PageView]);
33
+ LinkedStorage.setDefaultDataset(appData);
34
+
35
+ // File asset URLs.
36
+ LinkedFileStorage.setDefaultAccessURL(getAccessUrlLocalFileStore());
@@ -0,0 +1,12 @@
1
+ import { linkedPackage } from '@_linked/core/utils/Package';
2
+
3
+ export const {
4
+ linkedComponent,
5
+ linkedSetComponent,
6
+ linkedShape,
7
+ linkedUtil,
8
+ linkedOntology,
9
+ registerPackageExport,
10
+ packageExports,
11
+ packageName,
12
+ } = linkedPackage('${hyphen_name}');
@@ -0,0 +1,23 @@
1
+ @import '@_linked/css/package.css';
2
+
3
+ .Home {
4
+ display: block;
5
+ margin: --spacing(4) 0;
6
+ }
7
+
8
+ .Home code {
9
+ font-family: var(--font-mono);
10
+ background: var(--bg-section);
11
+ padding: 0 --spacing(1);
12
+ border-radius: var(--radius-element);
13
+ font-size: 0.85em;
14
+ }
15
+
16
+ .Home pre {
17
+ padding: --spacing(4);
18
+ background: var(--bg-section);
19
+ border-radius: var(--radius-element);
20
+ overflow-x: auto;
21
+ font-family: var(--font-mono);
22
+ margin: --spacing(2) 0;
23
+ }
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import { DefaultLayout } from '../layout/DefaultLayout';
3
+ import { PersonOverview } from '../components/PersonOverview';
4
+ import style from './Home.module.css';
5
+
6
+ export default function Home() {
7
+ return (
8
+ <DefaultLayout>
9
+ <div className={style.Home}>
10
+ <PersonOverview />
11
+ </div>
12
+ </DefaultLayout>
13
+ );
14
+ }
@@ -0,0 +1,18 @@
1
+ @import '@_linked/css/package.css';
2
+
3
+ .Card {
4
+ background: var(--bg-card);
5
+ border-radius: var(--radius-card);
6
+ box-shadow: var(--shadow-card);
7
+ padding: --spacing(6);
8
+ }
9
+
10
+ .Card h2 {
11
+ margin: 0 0 --spacing(3);
12
+ }
13
+
14
+ .Card p {
15
+ color: var(--color-gray-700);
16
+ line-height: 1.5;
17
+ margin: 0;
18
+ }
@@ -0,0 +1,16 @@
1
+ import { DefaultLayout } from '../layout/DefaultLayout';
2
+ import style from './Page1.module.css';
3
+
4
+ export default function Page1() {
5
+ return (
6
+ <DefaultLayout>
7
+ <div className={style.Card}>
8
+ <h2>Page 1</h2>
9
+ <p>
10
+ A second route to show how navigation works. Edit{' '}
11
+ <code>src/pages/Page1.tsx</code> to make this your own.
12
+ </p>
13
+ </div>
14
+ </DefaultLayout>
15
+ );
16
+ }
@@ -0,0 +1,19 @@
1
+ @import '@_linked/css/package.css';
2
+
3
+ .Card {
4
+ background: var(--bg-card);
5
+ border-radius: var(--radius-card);
6
+ box-shadow: var(--shadow-card);
7
+ padding: --spacing(6);
8
+ text-align: center;
9
+ }
10
+
11
+ .Card h2 {
12
+ margin: 0 0 --spacing(3);
13
+ }
14
+
15
+ .Card p {
16
+ color: var(--color-gray-700);
17
+ line-height: 1.5;
18
+ margin: 0;
19
+ }
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import { DefaultLayout } from '../layout/DefaultLayout';
3
+ import style from './PageNotFound.module.css';
4
+
5
+ export default function PageNotFound() {
6
+ return (
7
+ <DefaultLayout>
8
+ <div className={style.Card}>
9
+ <h2>Page not found</h2>
10
+ <p>The page you were looking for does not exist.</p>
11
+ </div>
12
+ </DefaultLayout>
13
+ );
14
+ }
@@ -0,0 +1,20 @@
1
+ @import '@_linked/css/package.css';
2
+
3
+ .Card {
4
+ background: var(--bg-card);
5
+ border-radius: var(--radius-card);
6
+ box-shadow: var(--shadow-card);
7
+ padding: --spacing(6);
8
+ max-width: 480px;
9
+ margin: --spacing(8) auto;
10
+ }
11
+
12
+ .Card h2 {
13
+ margin: 0 0 --spacing(3);
14
+ }
15
+
16
+ .Card p {
17
+ color: var(--color-gray-700);
18
+ line-height: 1.5;
19
+ margin: 0;
20
+ }
@@ -0,0 +1,16 @@
1
+ import { DefaultLayout } from '../layout/DefaultLayout';
2
+ import style from './Signin.module.css';
3
+
4
+ export default function Signin() {
5
+ return (
6
+ <DefaultLayout>
7
+ <div className={style.Card}>
8
+ <h2>Sign in</h2>
9
+ <p>
10
+ See <a href="https://linked.cm">linked.cm</a> for different ways to
11
+ sign in.
12
+ </p>
13
+ </div>
14
+ </DefaultLayout>
15
+ );
16
+ }
@@ -0,0 +1,85 @@
1
+ import { RequireAuth } from '@_linked/auth/components/RequireAuth';
2
+ import type { RoutesConfig } from '@_linked/server/types/RouteConfig';
3
+ import React, { lazy, Suspense } from 'react';
4
+ import { Route, Routes } from 'react-router-dom';
5
+ import { Spinner } from './components/Spinner';
6
+ import PageNotFound from './pages/PageNotFound';
7
+ import { lazyWithPreload } from './utils/lazyWithPreload';
8
+
9
+ // Create preloadable lazy components for direct-entry routes
10
+ const HomePage = lazyWithPreload(
11
+ () => import(/* webpackChunkName: "home" */ './pages/Home')
12
+ );
13
+
14
+ const SigninPage = lazyWithPreload(
15
+ () => import(/* webpackChunkName: "signin" */ './pages/Signin')
16
+ );
17
+
18
+ // Export preloadable components for server-side preloading
19
+ export const PRELOADABLE_ROUTES = {
20
+ home: HomePage,
21
+ signin: SigninPage,
22
+ };
23
+
24
+
25
+ export const ROUTES: RoutesConfig = {
26
+ home: {
27
+ path: '/',
28
+ component: HomePage.Component,
29
+ label: 'Home',
30
+ preloadChunks: ['home'],
31
+ },
32
+ page1: {
33
+ path: '/page1',
34
+ component: lazy(
35
+ () => import(/* webpackChunkName: "page1" */ './pages/Page1')
36
+ ),
37
+ label: 'Protected page',
38
+ requireAuth: true,
39
+ preloadChunks: ['page1'],
40
+ },
41
+ signin: {
42
+ path: '/signin',
43
+ component: SigninPage.Component,
44
+ label: 'Sign In',
45
+ excludeFromMenu: true,
46
+ preloadChunks: ['signin'],
47
+ },
48
+ };
49
+
50
+ export default function AppRoutes() {
51
+ return (
52
+ <Routes>
53
+ {Object.keys(ROUTES).map((routeName) => {
54
+ const route = ROUTES[routeName];
55
+ const Component = route.component;
56
+
57
+ //if a route is marked as requireAuth, wrap it in the RequireAuth component and pass the signinRoute
58
+ const AuthGuard = route.requireAuth ? RequireAuth : React.Fragment;
59
+ const authProps = route.requireAuth
60
+ ? { signinRoute: ROUTES.signin.path }
61
+ : {};
62
+
63
+ // define a render function that determines what to render based on the component and route.render
64
+ const renderRoute = () =>
65
+ // if a Component is defined, render it using JSX syntax (<Component />)
66
+ // if not, check if a route.render function is defined and call that render function if available.
67
+ // if neither Component nor route.render is defined, return null
68
+ Component ? <Component /> : route.render ? route.render() : null;
69
+
70
+ return (
71
+ <Route
72
+ key={route.path}
73
+ path={route.path}
74
+ element={
75
+ <AuthGuard {...authProps}>
76
+ <Suspense fallback={<Spinner />}>{renderRoute()}</Suspense>
77
+ </AuthGuard>
78
+ }
79
+ />
80
+ );
81
+ })}
82
+ <Route path="*" element={<PageNotFound />} />
83
+ </Routes>
84
+ );
85
+ }
@@ -0,0 +1,91 @@
1
+ /*
2
+ * Theme overrides for ${name}.
3
+ *
4
+ * The default palette below is a teal-blue primary + mint-green secondary.
5
+ * Override --color-primary-* / --color-secondary-* to your own brand.
6
+ *
7
+ * Available tokens (palette, surfaces, shadows, radii, spacing) are documented in:
8
+ * https://github.com/linked-cm/css/blob/main/README.md
9
+ * https://github.com/linked-cm/css/blob/main/docs/styling-and-themes.md
10
+ */
11
+
12
+ /* Tailwind v4 layers — split-import pattern to substitute the Linked preflight.
13
+ See @_linked/css README "Why the split-import?" for details. */
14
+ @layer theme, base, components, utilities;
15
+ @import 'tailwindcss/theme.css' layer(theme);
16
+ @import '@_linked/css/preflight.css';
17
+ @import 'tailwindcss/utilities.css' layer(utilities);
18
+
19
+ /* Apply Tailwind config from @_linked/cli — ensures all @_linked/* shapes
20
+ are scanned for utility classes. */
21
+ @config "@_linked/cli/tailwind.config.js";
22
+
23
+ /* @_linked/css: framework theme defaults + utility mixins.
24
+ App-specific @theme overrides below extend / replace these defaults. */
25
+ @import '@_linked/css/theme-defaults.css';
26
+ @import '@_linked/css/utilities.css';
27
+
28
+ @theme {
29
+ /* === Primary palette — Linked teal-blue === */
30
+ --color-primary-50: #f7fbfd;
31
+ --color-primary-100: #eef7fb;
32
+ --color-primary-200: #d3ebf5;
33
+ --color-primary-300: #a8d7ec;
34
+ --color-primary-400: #71bbdd;
35
+ --color-primary-500: #116d8d; /* base */
36
+ --color-primary-600: #0a5f7d;
37
+ --color-primary-700: #005970;
38
+ --color-primary-800: #004d61;
39
+ --color-primary-900: #001f28;
40
+
41
+ /* === Secondary palette — mint green === */
42
+ --color-secondary-50: #f9fdfc;
43
+ --color-secondary-100: #f4fbf8;
44
+ --color-secondary-200: #e6f5ef;
45
+ --color-secondary-300: #ccebe0;
46
+ --color-secondary-400: #b1e0cd;
47
+ --color-secondary-500: #52a181; /* base */
48
+ --color-secondary-600: #297a5c;
49
+ --color-secondary-700: #146b4f;
50
+ --color-secondary-800: #005d43;
51
+ --color-secondary-900: #00201a;
52
+
53
+ /* === Shadows === */
54
+ --shadow-surface: var(--shadow-sm); /* Background panels, sections */
55
+ --shadow-card: var(--shadow-md); /* Cards, organized content blocks */
56
+ --shadow-popover: var(--shadow-lg); /* Tooltips, dropdowns, popovers */
57
+ --shadow-modal: var(--shadow-2xl); /* Modals, overlays */
58
+ --shadow-floating-action: var(--shadow-lg);
59
+ --shadow-content: none; /* Buttons, inputs inside surfaces */
60
+
61
+ /* === Backgrounds / Surfaces === */
62
+ --bg-page: var(--color-gray-50);
63
+ --bg-section: var(--color-gray-100);
64
+ --bg-card: var(--color-white);
65
+ --bg-popover: var(--color-white);
66
+ --bg-modal: var(--color-white);
67
+ --bg-sidebar: var(--color-gray-200);
68
+ --bg-footer: var(--color-gray-300);
69
+ --bg-overlay: var(--color-gray-900);
70
+ --color-card: var(--color-white);
71
+
72
+ /* === Border radius === */
73
+ --radius-surface: var(--radius-sm);
74
+ --radius-card: var(--radius-lg);
75
+ --radius-element: var(--radius-md);
76
+ --radius-popover: var(--radius-md);
77
+ --radius-modal: var(--radius-2xl);
78
+ --radius-floating-action: var(--radius-full);
79
+ --radius-none: 0px;
80
+
81
+ /* === Z-index === */
82
+ --z-base: 0;
83
+ --z-surface: 10;
84
+ --z-popover: 30;
85
+ --z-modal: 50;
86
+ --z-overlay: 75;
87
+ --z-tooltip: 100;
88
+
89
+ /* === Spacing tokens === */
90
+ --spacing-card: calc(var(--spacing) * 4);
91
+ }
@@ -0,0 +1,14 @@
1
+ declare module '*.css' {
2
+ const styles: {
3
+ readonly [key: string]: string;
4
+ };
5
+ // export type ClassName = string;
6
+ export default styles;
7
+ }
8
+ declare module '*.scss' {
9
+ const styles: {
10
+ readonly [key: string]: string;
11
+ };
12
+ // export type ClassName = string;
13
+ export default styles;
14
+ }
@@ -0,0 +1,42 @@
1
+ import { ComponentType, lazy } from 'react';
2
+
3
+ export interface PreloadableComponent<T extends ComponentType<any>> {
4
+ preload: () => Promise<{ default: T }>;
5
+ Component: React.LazyExoticComponent<T>;
6
+ }
7
+
8
+ /**
9
+ * Creates a lazy-loaded component with a preload function
10
+ * This allows preloading the component before React needs it
11
+ *
12
+ * @example
13
+ * const SigninPage = lazyWithPreload(() => import('./pages/Signin'));
14
+ * // Later, before navigation or on server:
15
+ * SigninPage.preload();
16
+ */
17
+ export function lazyWithPreload<T extends ComponentType<any>>(
18
+ importFunc: () => Promise<{ default: T }>
19
+ ): PreloadableComponent<T> {
20
+ let modulePromise: Promise<{ default: T }> | null = null;
21
+
22
+ const preload = () => {
23
+ if (!modulePromise) {
24
+ modulePromise = importFunc();
25
+ }
26
+ return modulePromise;
27
+ };
28
+
29
+ const Component = lazy(() => {
30
+ // If preload was called, reuse the same promise
31
+ // This ensures React uses the already-loaded module
32
+ if (!modulePromise) {
33
+ modulePromise = importFunc();
34
+ }
35
+ return modulePromise;
36
+ });
37
+
38
+ return {
39
+ preload,
40
+ Component,
41
+ };
42
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Client-side script to preload lazy routes before hydration
3
+ * This should be called BEFORE React's hydrateRoot()
4
+ */
5
+
6
+ // This will be populated by the server with the routes that need preloading
7
+ declare global {
8
+ interface Window {
9
+ __PRELOAD_ROUTES__?: string[];
10
+ }
11
+ }
12
+
13
+ export async function preloadMatchedRoute() {
14
+ // Get the list of routes to preload from the server
15
+ const routesToPreload = window.__PRELOAD_ROUTES__ || [];
16
+
17
+ if (routesToPreload.length === 0) {
18
+ return;
19
+ }
20
+
21
+ // Dynamically import the routes module to access PRELOADABLE_ROUTES
22
+ const routesModule = await import('../routes');
23
+ const { PRELOADABLE_ROUTES } = routesModule;
24
+
25
+ // Preload each route
26
+ const preloadPromises = routesToPreload
27
+ .map((routeKey) => {
28
+ const preloadableRoute = PRELOADABLE_ROUTES[routeKey];
29
+ if (preloadableRoute && preloadableRoute.preload) {
30
+ return preloadableRoute.preload();
31
+ }
32
+ return null;
33
+ })
34
+ .filter(Boolean);
35
+
36
+ // Wait for all preloads to complete
37
+ await Promise.all(preloadPromises);
38
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "esnext",
4
+ "moduleResolution": "node",
5
+ "sourceMap": true,
6
+ "target": "es2018",
7
+ "resolveJsonModule": true,
8
+ "declaration": false,
9
+ "experimentalDecorators": true,
10
+ "emitDecoratorMetadata": true,
11
+ "esModuleInterop": true,
12
+ "resolveJsonModule": true,
13
+ "jsx": "react-jsx",
14
+ "types": ["node", "react", "react-dom"],
15
+ "plugins": [
16
+ {
17
+ "name": "typescript-plugin-css-modules"
18
+ }
19
+ ],
20
+ "pretty": true
21
+ },
22
+ "files": ["./src/index.tsx", "./src/types.ts"],
23
+ "include": ["./src/backend.ts"]
24
+ }
@@ -18,9 +18,11 @@
18
18
  "license": "ISC",
19
19
  "scripts": {
20
20
  "start": "yarn linked dev",
21
- "build": "yarn linked build",
22
- "prepublishOnly": "yarn linked build production",
23
- "postpublish": "yarn linked register"
21
+ "build": "rimraf ./lib && yarn build-esm && yarn build-cjs && yarn copy-to-lib && yarn dual-package",
22
+ "build-esm": "echo \"compiling ESM\" && rimraf ./lib/esm && yarn tsc -p tsconfig-esm.json",
23
+ "build-cjs": "echo \"compiling CJS\" && rimraf ./lib/cjs && yarn tsc -p tsconfig-cjs.json",
24
+ "copy-to-lib": "echo 'copying CSS + JSON assets to lib folder' && yarn copyfiles -u 1 'src/**/*.css' 'src/**/*.json' lib/esm && yarn copyfiles -u 1 'src/**/*.css' 'src/**/*.json' lib/cjs",
25
+ "dual-package": "echo 'adding package.json to each lib folder for dual output support' && yarn tsconfig-to-dual-package ./tsconfig-cjs.json ./tsconfig-esm.json"
24
26
  },
25
27
  "keywords": [
26
28
  "linked",
@@ -58,6 +60,8 @@
58
60
  "devDependencies": {
59
61
  "@types/node": "^22.13.14",
60
62
  "@_linked/cli": "^1.2",
63
+ "copyfiles": "^2.4.1",
64
+ "rimraf": "^5.0.7",
61
65
  "tsconfig-to-dual-package": "^1.2.0",
62
66
  "typescript": "^5.7.3"
63
67
  }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env tsx
2
+ //# sourceMappingURL=cli-deprecated.d.ts.map