rimecms 0.30.3 → 0.31.1
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/README.md +1 -39
- package/dist/.rime-modules.d.ts +23 -0
- package/dist/.rime-modules.json +1 -0
- package/dist/adapter-sqlite/generate-schema/write.server.js +6 -6
- package/dist/core/areas/config/builder.server.d.ts +0 -1
- package/dist/core/collections/auth/hooks/before-upsert/augment-fields-password.server.d.ts +1 -1
- package/dist/core/collections/config/builder.server.d.ts +0 -1
- package/dist/core/collections/upload/hooks/convert-base64.server.d.ts +1 -1
- package/dist/core/collections/upload/hooks/extract-path.server.d.ts +1 -1
- package/dist/core/collections/upload/hooks/handle-path-creation.server.d.ts +1 -1
- package/dist/core/collections/upload/hooks/process-file-upload.server.d.ts +1 -1
- package/dist/core/config/auth/better-auth.server.d.ts +3 -3
- package/dist/core/config/client/augment-plugins.d.ts +1 -5
- package/dist/core/config/client/augment-plugins.js +3 -3
- package/dist/core/config/client/build-config.d.ts +1 -5
- package/dist/core/config/config-context.server.d.ts +1 -1
- package/dist/core/config/server/augment-plugins.server.d.ts +2 -42
- package/dist/core/config/server/augment-plugins.server.js +11 -6
- package/dist/core/config/server/build-config.server.d.ts +9 -4007
- package/dist/core/config/types.d.ts +12 -11
- package/dist/core/dev/cache/index.server.js +1 -1
- package/dist/core/dev/cli/clear/index.server.js +2 -0
- package/dist/core/dev/cli/generate/index.server.js +4 -0
- package/dist/core/dev/cli/generate-manifest/index.server.d.ts +17 -0
- package/dist/core/dev/cli/generate-manifest/index.server.js +221 -0
- package/dist/core/dev/cli/index.js +42 -0
- package/dist/core/dev/cli/init/index.server.js +11 -4
- package/dist/core/dev/cli/init/templates.d.ts +1 -1
- package/dist/core/dev/cli/init/templates.js +5 -3
- package/dist/core/dev/cli/util/package-manager.server.js +5 -3
- package/dist/core/dev/constants.d.ts +10 -0
- package/dist/core/dev/constants.js +15 -0
- package/dist/core/dev/generate/routes/common.server.js +4 -7
- package/dist/core/dev/generate/routes/index.server.js +25 -4
- package/dist/core/dev/generate/runtime/index.server.d.ts +34 -14
- package/dist/core/dev/generate/runtime/index.server.js +56 -57
- package/dist/core/dev/generate/runtime/parse-exports.server.d.ts +7 -0
- package/dist/core/dev/generate/runtime/parse-exports.server.js +40 -0
- package/dist/core/dev/generate/sanitize/index.server.d.ts +0 -7
- package/dist/core/dev/generate/sanitize/index.server.js +57 -5
- package/dist/core/dev/generate/types/index.server.js +16 -39
- package/dist/core/dev/vite/index.server.d.ts +1 -0
- package/dist/core/dev/vite/index.server.js +203 -42
- package/dist/core/ensure.server.d.ts +4 -3
- package/dist/core/ensure.server.js +8 -8
- package/dist/core/handlers/index.js +2 -2
- package/dist/core/operations/hooks/before-upsert/data-config-map.server.d.ts +1 -1
- package/dist/core/operations/hooks/before-upsert/handle-new-version.server.d.ts +1 -1
- package/dist/core/operations/hooks/before-upsert/original-config-map.server.d.ts +1 -1
- package/dist/core/operations/hooks/before-upsert/set-default-values.server.d.ts +1 -1
- package/dist/core/operations/hooks/before-upsert/validate-fields.server.d.ts +1 -1
- package/dist/core/operations/hooks/index.server.d.ts +0 -4
- package/dist/core/operations/hooks/index.server.js +0 -4
- package/dist/core/plugins/api-init/index.d.ts +2 -0
- package/dist/core/plugins/api-init/index.js +1 -0
- package/dist/core/plugins/api-init/{index.server.d.ts → module.server.d.ts} +0 -1
- package/dist/core/plugins/api-init/{index.server.js → module.server.js} +0 -1
- package/dist/core/plugins/cache/index.d.ts +2 -5
- package/dist/core/plugins/cache/index.js +1 -21
- package/dist/core/plugins/cache/module.d.ts +4 -0
- package/dist/core/plugins/cache/module.js +22 -0
- package/dist/core/plugins/cache/{index.server.d.ts → module.server.d.ts} +1 -2
- package/dist/core/plugins/cache/{index.server.js → module.server.js} +0 -1
- package/dist/core/plugins/index.d.ts +1 -8
- package/dist/core/plugins/index.js +0 -3
- package/dist/core/plugins/mailer/index.d.ts +2 -0
- package/dist/core/plugins/mailer/index.js +1 -0
- package/dist/core/plugins/mailer/{index.server.d.ts → module.server.d.ts} +0 -1
- package/dist/core/plugins/mailer/{index.server.js → module.server.js} +0 -1
- package/dist/core/plugins/sse/index.d.ts +2 -0
- package/dist/core/plugins/sse/index.js +1 -0
- package/dist/core/plugins/sse/{index.server.d.ts → module.server.d.ts} +0 -1
- package/dist/core/plugins/sse/{index.server.js → module.server.js} +0 -1
- package/dist/core/rime.server.d.ts +16 -16
- package/dist/fields/link/index.js +59 -56
- package/dist/fields/relation/index.js +63 -70
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/panel/context/config.svelte.d.ts +2 -2
- package/dist/panel/context/documentForm.svelte.d.ts +2 -2
- package/dist/panel/context/live.svelte.js +1 -1
- package/dist/panel/pages/auth/sign-in/SignIn.svelte +6 -1
- package/dist/server.d.ts +4 -4
- package/dist/server.js +4 -4
- package/dist/types.d.ts +1 -1
- package/package.json +4 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Adapter } from '../../adapter-sqlite/index.server.js';
|
|
2
2
|
import type { PanelLanguage } from '../i18n/index.js';
|
|
3
3
|
import type { Hook, HookBeforeOperation } from '../operations/hooks/index.server.js';
|
|
4
|
-
import type { Plugin
|
|
5
|
-
import type { SMTPConfig } from '../plugins/mailer/
|
|
4
|
+
import type { Plugin } from '../plugins/index.js';
|
|
5
|
+
import type { SMTPConfig } from '../plugins/mailer/module.server.js';
|
|
6
6
|
import type { Field, Option } from '../../fields/types.js';
|
|
7
7
|
import type { RegisterArea, RegisterCollection } from '../../index.js';
|
|
8
8
|
import type { DashboardEntry } from '../../panel/pages/dashboard/types.js';
|
|
@@ -79,10 +79,9 @@ export interface Config {
|
|
|
79
79
|
* }
|
|
80
80
|
*/
|
|
81
81
|
$routes?: Record<string, RouteConfig>;
|
|
82
|
-
/** List of
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
$plugins?: Plugin[];
|
|
82
|
+
/** List of plugins — safety for anything server-only inside one comes from the
|
|
83
|
+
* $rime/.../module convention, not from a $-prefix on this key */
|
|
84
|
+
plugins?: Plugin[];
|
|
86
85
|
/** Custom object for server-only config additional values */
|
|
87
86
|
$custom?: Record<string, any>;
|
|
88
87
|
/** Custom object for both client and server config additional values */
|
|
@@ -309,7 +308,7 @@ export type BuiltCollection = Omit<Collection<string>, 'icon' | 'versions' | 'up
|
|
|
309
308
|
_generateSchema?: false;
|
|
310
309
|
_generateRoutes?: false;
|
|
311
310
|
};
|
|
312
|
-
export type BuiltAreaClient =
|
|
311
|
+
export type BuiltAreaClient = BuiltArea;
|
|
313
312
|
export type BuiltArea = Omit<Area<string>, 'versions'> & {
|
|
314
313
|
slug: AreaSlug;
|
|
315
314
|
type: 'area';
|
|
@@ -326,7 +325,7 @@ export type BuiltArea = Omit<Area<string>, 'versions'> & {
|
|
|
326
325
|
_generateSchema?: false;
|
|
327
326
|
_generateRoutes?: false;
|
|
328
327
|
};
|
|
329
|
-
export type BuiltCollectionClient =
|
|
328
|
+
export type BuiltCollectionClient = BuiltCollection;
|
|
330
329
|
export type BuiltConfig = {
|
|
331
330
|
/** Database location relative to the root project ex: ./db/my-app.sqlite */
|
|
332
331
|
$database: string;
|
|
@@ -341,8 +340,10 @@ export type BuiltConfig = {
|
|
|
341
340
|
icons: Record<string, any>;
|
|
342
341
|
$trustedOrigins: string[];
|
|
343
342
|
$routes?: Record<string, RouteConfig>;
|
|
343
|
+
/** Fully-resolved plugin list, computed by augmentPluginsServer — internal, not the
|
|
344
|
+
* consumer-facing config key (that's just `plugins`) */
|
|
344
345
|
$plugins?: Plugin[];
|
|
345
|
-
plugins?:
|
|
346
|
+
plugins?: Plugin[];
|
|
346
347
|
panel: {
|
|
347
348
|
routes: Record<string, CustomPanelRoute>;
|
|
348
349
|
navigation: NavigationConfig;
|
|
@@ -366,12 +367,12 @@ export type BuiltConfig = {
|
|
|
366
367
|
$custom?: Record<string, any>;
|
|
367
368
|
custom?: Record<string, any>;
|
|
368
369
|
};
|
|
369
|
-
export type ServerConfigProps = '$adapter' | '$database' | '$trustedOrigins' | '$routes' | '$smtp' | '$custom' | '$
|
|
370
|
+
export type ServerConfigProps = '$adapter' | '$database' | '$trustedOrigins' | '$routes' | '$smtp' | '$custom' | '$auth';
|
|
370
371
|
export type SanitizedConfigClient = Omit<Config, ServerConfigProps | 'collections' | 'areas'> & {
|
|
371
372
|
collections?: BuiltCollectionClient[];
|
|
372
373
|
areas?: BuiltAreaClient[];
|
|
373
374
|
};
|
|
374
|
-
export type BuiltConfigClient = Omit<BuiltConfig, ServerConfigProps | 'panel' | 'collections' | 'areas'> & {
|
|
375
|
+
export type BuiltConfigClient = Omit<BuiltConfig, ServerConfigProps | '$plugins' | 'panel' | 'collections' | 'areas'> & {
|
|
375
376
|
collections: BuiltCollectionClient[];
|
|
376
377
|
areas: BuiltAreaClient[];
|
|
377
378
|
icons: Dic<Component<IconProps>>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { RIME_DEV_CACHE_DIR } from '../../constant.server.js';
|
|
2
1
|
import fs from 'fs';
|
|
3
2
|
import path from 'path';
|
|
3
|
+
import { RIME_DEV_CACHE_DIR } from '../../constant.server.js';
|
|
4
4
|
const dev = process.env.NODE_ENV === 'development';
|
|
5
5
|
if (!fs.existsSync(RIME_DEV_CACHE_DIR) && dev) {
|
|
6
6
|
fs.mkdirSync(RIME_DEV_CACHE_DIR);
|
|
@@ -12,6 +12,7 @@ const clearMessage = `Are you sure you want to delete all related rime files (Y/
|
|
|
12
12
|
- ./src/lib/${OUTPUT_DIR}
|
|
13
13
|
- ./src/app.generated.d.ts
|
|
14
14
|
- ./src/rime.generated.d.ts
|
|
15
|
+
- ./src/rime.modules.generated.d.ts
|
|
15
16
|
- ./src/hooks.server.ts
|
|
16
17
|
- ./drizzle.config.ts
|
|
17
18
|
`;
|
|
@@ -35,6 +36,7 @@ export const clear = async (args) => {
|
|
|
35
36
|
rmSync(path.join('src', 'hooks.server.ts'), { force: true });
|
|
36
37
|
rmSync(path.join('src', 'app.generated.d.ts'), { force: true });
|
|
37
38
|
rmSync(path.join('src', 'rime.generated.d.ts'), { force: true });
|
|
39
|
+
rmSync(path.join('src', 'rime.modules.generated.d.ts'), { force: true });
|
|
38
40
|
rmSync(path.join('drizzle.config.ts'), { force: true });
|
|
39
41
|
return logger.info('rime cleared');
|
|
40
42
|
};
|
|
@@ -2,6 +2,7 @@ import { RIME_DEV_CACHE_DIR } from '../../../constant.server.js';
|
|
|
2
2
|
import cache from '../../cache/index.server.js';
|
|
3
3
|
import { sanitize } from '../../generate/sanitize/index.server.js';
|
|
4
4
|
import { ensureGeneratedConfig, ensureUserConfigExist } from '../../../ensure.server.js';
|
|
5
|
+
import { regenerateModulesDeclaration } from '../../vite/index.server.js';
|
|
5
6
|
import { logger } from '../../../logger/index.server.js';
|
|
6
7
|
import { trycatch } from '../../../../util/function.js';
|
|
7
8
|
import { mkdirSync, rmSync } from 'fs';
|
|
@@ -67,6 +68,9 @@ export const generate = async (args) => {
|
|
|
67
68
|
try {
|
|
68
69
|
ensureUserConfigExist();
|
|
69
70
|
await sanitizeConfig();
|
|
71
|
+
// Normal `vite dev` writes this from `configureServer`'s `listening` event, which never
|
|
72
|
+
// fires here — this command's own Vite server runs in `middlewareMode` (no `httpServer`).
|
|
73
|
+
regenerateModulesDeclaration();
|
|
70
74
|
const importPathJS = ensureGeneratedConfig();
|
|
71
75
|
logger.info('Starting vite server...');
|
|
72
76
|
const vite = await createServer();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run at prepack, after `svelte-package` (operates on the compiled `dist/`, not `src/lib`) —
|
|
3
|
+
* makes this package's own `$rime/modules` splits consumable by anyone who installs it:
|
|
4
|
+
*
|
|
5
|
+
* 1. Builds an export-name → split index from every module.(server.)js pair under `dist/`,
|
|
6
|
+
* validating it as it goes (see `buildModuleIndex` below) — a name that resolves
|
|
7
|
+
* ambiguously has no correct rewrite target in step 2, so this has to fail loudly here,
|
|
8
|
+
* not silently later.
|
|
9
|
+
* 2. AST-rewrites every `import ... from '$rime/modules'` found anywhere under `dist/` into
|
|
10
|
+
* one `import ... from '$rime/modules/<pkg>/<subpath>'` per split it actually draws from —
|
|
11
|
+
* the bare barrel form never survives past this step.
|
|
12
|
+
* 3. Writes `dist/.rime-modules.json` (subpath → real file paths, read directly by a
|
|
13
|
+
* consumer's Vite plugin — no fs.existsSync probing, no guessing) and
|
|
14
|
+
* `dist/.rime-modules.d.ts` (one `declare module '$rime/modules/<pkg>/<subpath>'` per
|
|
15
|
+
* split, referenced by a consumer's generated types).
|
|
16
|
+
*/
|
|
17
|
+
export declare const generateManifest: () => void;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { generate } from '@babel/generator';
|
|
2
|
+
import * as t from '@babel/types';
|
|
3
|
+
import { babelParse } from 'ast-kit';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { logger } from '../../../logger/index.server.js';
|
|
7
|
+
import { scanModulePairs } from '../../generate/runtime/index.server.js';
|
|
8
|
+
import { parseExportNames } from '../../generate/runtime/parse-exports.server.js';
|
|
9
|
+
import { getPackageInfoByKey } from '../util/package.server.js';
|
|
10
|
+
/**
|
|
11
|
+
* Run at prepack, after `svelte-package` (operates on the compiled `dist/`, not `src/lib`) —
|
|
12
|
+
* makes this package's own `$rime/modules` splits consumable by anyone who installs it:
|
|
13
|
+
*
|
|
14
|
+
* 1. Builds an export-name → split index from every module.(server.)js pair under `dist/`,
|
|
15
|
+
* validating it as it goes (see `buildModuleIndex` below) — a name that resolves
|
|
16
|
+
* ambiguously has no correct rewrite target in step 2, so this has to fail loudly here,
|
|
17
|
+
* not silently later.
|
|
18
|
+
* 2. AST-rewrites every `import ... from '$rime/modules'` found anywhere under `dist/` into
|
|
19
|
+
* one `import ... from '$rime/modules/<pkg>/<subpath>'` per split it actually draws from —
|
|
20
|
+
* the bare barrel form never survives past this step.
|
|
21
|
+
* 3. Writes `dist/.rime-modules.json` (subpath → real file paths, read directly by a
|
|
22
|
+
* consumer's Vite plugin — no fs.existsSync probing, no guessing) and
|
|
23
|
+
* `dist/.rime-modules.d.ts` (one `declare module '$rime/modules/<pkg>/<subpath>'` per
|
|
24
|
+
* split, referenced by a consumer's generated types).
|
|
25
|
+
*/
|
|
26
|
+
export const generateManifest = () => {
|
|
27
|
+
const pkgName = getPackageInfoByKey('name');
|
|
28
|
+
if (!pkgName) {
|
|
29
|
+
throw new Error("$rime generate-manifest: could not read this package's own name from package.json");
|
|
30
|
+
}
|
|
31
|
+
const distDir = path.resolve(process.cwd(), 'dist');
|
|
32
|
+
if (!fs.existsSync(distDir)) {
|
|
33
|
+
throw new Error(`$rime generate-manifest: no dist/ at ${distDir} — run this after svelte-package, not before`);
|
|
34
|
+
}
|
|
35
|
+
const pairs = scanModulePairs(distDir);
|
|
36
|
+
const nameOwner = buildModuleIndex(pairs);
|
|
37
|
+
rewriteBarrelImports(distDir, pkgName, nameOwner);
|
|
38
|
+
writeManifest(distDir, pkgName, pairs);
|
|
39
|
+
logger.info(`[✓] $rime/modules manifest generated for ${pkgName} (${pairs.size} split(s))`);
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Export-name → split index, with the two collision scenarios from the design doc handled
|
|
43
|
+
* distinctly:
|
|
44
|
+
* - module.ts and module.server.ts of the *same* split sharing a name — not a collision, that's
|
|
45
|
+
* the whole point of a pair (skip, both sides collapse to one entry). The two sides don't
|
|
46
|
+
* need to export the *same set* of names either — a server-only helper the client build
|
|
47
|
+
* never needs (e.g. cache's own `toHash`) is completely normal, not a broken pair; indexed
|
|
48
|
+
* from the union of both sides, worth a log for visibility, never a blocking error.
|
|
49
|
+
* - two *different* splits exporting the same name — the actual collision. An ambiguous
|
|
50
|
+
* `$rime/modules` import has no correct rewrite target, so this is a hard error here, not a
|
|
51
|
+
* warning: it has to be caught before the rewrite runs, not after.
|
|
52
|
+
*/
|
|
53
|
+
function buildModuleIndex(pairs) {
|
|
54
|
+
const nameOwner = new Map();
|
|
55
|
+
for (const [subpath, entry] of pairs) {
|
|
56
|
+
const serverNames = entry.server ? parseExportNames(entry.server) : [];
|
|
57
|
+
const clientNames = entry.client ? parseExportNames(entry.client) : [];
|
|
58
|
+
if (entry.server && entry.client) {
|
|
59
|
+
const serverSet = new Set(serverNames);
|
|
60
|
+
const clientSet = new Set(clientNames);
|
|
61
|
+
const onlyServer = serverNames.filter((name) => !clientSet.has(name));
|
|
62
|
+
const onlyClient = clientNames.filter((name) => !serverSet.has(name));
|
|
63
|
+
if (onlyServer.length || onlyClient.length) {
|
|
64
|
+
logger.debug(`$rime/modules: ${subpath} — module.ts and module.server.ts export different names ` +
|
|
65
|
+
`(server-only: ${onlyServer.join(', ') || 'none'}, client-only: ${onlyClient.join(', ') || 'none'})`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const names = new Set([...serverNames, ...clientNames]);
|
|
69
|
+
for (const name of names) {
|
|
70
|
+
const existing = nameOwner.get(name);
|
|
71
|
+
if (existing && existing !== subpath) {
|
|
72
|
+
throw new Error(`$rime/modules: "${name}" exported by both ${existing} and ${subpath}`);
|
|
73
|
+
}
|
|
74
|
+
nameOwner.set(name, subpath);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return nameOwner;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* `dist/.rime-modules.json` (runtime, read by a consumer's Vite plugin) and
|
|
81
|
+
* `dist/.rime-modules.d.ts` (types, referenced by a consumer's generated types).
|
|
82
|
+
*
|
|
83
|
+
* Both store paths *relative to `distDir`*, not absolute — confirmed in practice that an
|
|
84
|
+
* absolute path here is a real bug, not just a style choice: it bakes in wherever `dist/`
|
|
85
|
+
* happened to sit on the machine that ran `generate-manifest`, and a consumer's Vite plugin
|
|
86
|
+
* (`vite/index.server.ts`'s `load()`, third-party qualified form) re-joins it onto the
|
|
87
|
+
* *installed* package's own root (`findInstalledPackageRoot`) at read time. On a real install
|
|
88
|
+
* that absolute path wouldn't even exist; on the same dev machine the package was authored on
|
|
89
|
+
* it silently reads the author's own working-copy `dist/` instead of what's actually installed
|
|
90
|
+
* in the consumer's `node_modules` - stale-looking field/behavior drift with no error raised.
|
|
91
|
+
*
|
|
92
|
+
* The `.d.ts` needs a *different* relative shape than the runtime manifest, verified directly
|
|
93
|
+
* (a real `tsc` run — a plain filesystem path, absolute or relative, silently resolves to zero
|
|
94
|
+
* exports inside a `declare module` block; only a bare package-qualified specifier, resolved
|
|
95
|
+
* through the same `node_modules` mechanism a real import would use, actually works): every
|
|
96
|
+
* `export *` target is `<pkgName>/dist/<subpath-relative-to-dist>`.
|
|
97
|
+
*/
|
|
98
|
+
function writeManifest(distDir, pkgName, pairs) {
|
|
99
|
+
const manifest = {};
|
|
100
|
+
const dts = [];
|
|
101
|
+
const relativeToDist = (target) => path.relative(distDir, target).split(path.sep).join('/');
|
|
102
|
+
for (const [subpath, entry] of pairs) {
|
|
103
|
+
manifest[subpath] = {
|
|
104
|
+
client: entry.client ? relativeToDist(entry.client) : undefined,
|
|
105
|
+
server: entry.server ? relativeToDist(entry.server) : undefined
|
|
106
|
+
};
|
|
107
|
+
const target = entry.server || entry.client;
|
|
108
|
+
const bareSpecifier = `${pkgName}/dist/${relativeToDist(target)}`.replace(/\.ts$/, '.js');
|
|
109
|
+
dts.push(`declare module '$rime/modules/${pkgName}/${subpath}' {\n export * from '${bareSpecifier}';\n}`);
|
|
110
|
+
}
|
|
111
|
+
fs.writeFileSync(path.join(distDir, '.rime-modules.json'), JSON.stringify(manifest));
|
|
112
|
+
fs.writeFileSync(path.join(distDir, '.rime-modules.d.ts'), dts.join('\n\n') + '\n');
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Walks every `.js`/`.d.ts` file under `dist/`, rewriting `import ... from '$rime/modules'`
|
|
116
|
+
* into one `import ... from '$rime/modules/<pkg>/<subpath>'` per split its specifiers actually
|
|
117
|
+
* draw from — a single barrel import can (and often will) pull names from several different
|
|
118
|
+
* splits at once, each becoming its own import statement.
|
|
119
|
+
*
|
|
120
|
+
* Every rewritten `.d.ts` file also gets a `/// <reference path="...">` to `.rime-modules.d.ts`
|
|
121
|
+
* prepended, making it self-contained — verified directly (a real `tsc` run): without it, a
|
|
122
|
+
* consumer never discovers the ambient `declare module '$rime/modules/<pkg>/...'` declarations
|
|
123
|
+
* at all, *even from a file inside this same package*, since nothing imports `.rime-modules.d.ts`
|
|
124
|
+
* and it's outside any project's own `include`. Doing it here means the *consumer* never needs
|
|
125
|
+
* to know this package exists just to get its types working — no eager, app-side aggregation
|
|
126
|
+
* of every installed rime-dependent package required.
|
|
127
|
+
*/
|
|
128
|
+
function rewriteBarrelImports(distDir, pkgName, nameOwner) {
|
|
129
|
+
const walk = (dir) => {
|
|
130
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
131
|
+
const fullPath = path.join(dir, entry.name);
|
|
132
|
+
if (entry.isDirectory()) {
|
|
133
|
+
walk(fullPath);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (!entry.name.endsWith('.js') && !entry.name.endsWith('.d.ts'))
|
|
137
|
+
continue;
|
|
138
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
139
|
+
if (!content.includes('$rime/modules'))
|
|
140
|
+
continue;
|
|
141
|
+
const ast = babelParse(content, 'ts', { sourceType: 'module', attachComment: true });
|
|
142
|
+
let changed = false;
|
|
143
|
+
const newBody = [];
|
|
144
|
+
for (const node of ast.body) {
|
|
145
|
+
// `import { x } from '$rime/modules'` (runtime .js) and the equivalent re-export
|
|
146
|
+
// `export { x } from '$rime/modules'` (declaration .d.ts, what tsc/svelte-package
|
|
147
|
+
// actually emits — a real end-to-end run caught this, not something guessable) both
|
|
148
|
+
// need the same treatment.
|
|
149
|
+
if (t.isImportDeclaration(node) && node.source.value === '$rime/modules') {
|
|
150
|
+
changed = true;
|
|
151
|
+
newBody.push(...splitBarrelImport(node, fullPath, pkgName, nameOwner));
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (t.isExportNamedDeclaration(node) && node.source?.value === '$rime/modules') {
|
|
155
|
+
changed = true;
|
|
156
|
+
newBody.push(...splitBarrelReExport(node, fullPath, pkgName, nameOwner));
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (t.isExportAllDeclaration(node) && node.source.value === '$rime/modules') {
|
|
160
|
+
throw new Error(`$rime/modules: ${fullPath} does 'export * from $rime/modules' — ambiguous, can't tell which split each re-exported name belongs to. Import/re-export specific names instead.`);
|
|
161
|
+
}
|
|
162
|
+
newBody.push(node);
|
|
163
|
+
}
|
|
164
|
+
if (changed) {
|
|
165
|
+
ast.body = newBody;
|
|
166
|
+
const { code } = generate(ast, { compact: false, comments: true });
|
|
167
|
+
fs.writeFileSync(fullPath, entry.name.endsWith('.d.ts') ? withManifestReference(code, fullPath, distDir) : code);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
walk(distDir);
|
|
172
|
+
}
|
|
173
|
+
function withManifestReference(code, fullPath, distDir) {
|
|
174
|
+
const relative = path
|
|
175
|
+
.relative(path.dirname(fullPath), path.join(distDir, '.rime-modules.d.ts'))
|
|
176
|
+
.split(path.sep)
|
|
177
|
+
.join('/');
|
|
178
|
+
const referencePath = relative.startsWith('.') ? relative : `./${relative}`;
|
|
179
|
+
return `/// <reference path="${referencePath}" />\n${code}`;
|
|
180
|
+
}
|
|
181
|
+
/** `import { x } from '$rime/modules'` (runtime .js) — groups specifiers by which split each
|
|
182
|
+
* name actually belongs to (via `nameOwner`, built by `buildModuleIndex`), one import
|
|
183
|
+
* statement per split. */
|
|
184
|
+
function splitBarrelImport(node, fullPath, pkgName, nameOwner) {
|
|
185
|
+
const bySubpath = new Map();
|
|
186
|
+
for (const spec of node.specifiers) {
|
|
187
|
+
if (!t.isImportSpecifier(spec)) {
|
|
188
|
+
throw new Error(`$rime/modules: ${fullPath} imports the barrel with a default/namespace specifier — only named imports are supported`);
|
|
189
|
+
}
|
|
190
|
+
const importedName = t.isIdentifier(spec.imported) ? spec.imported.name : spec.imported.value;
|
|
191
|
+
const subpath = nameOwner.get(importedName);
|
|
192
|
+
if (!subpath) {
|
|
193
|
+
throw new Error(`$rime/modules: '${importedName}' imported in ${fullPath} but not exported by any split`);
|
|
194
|
+
}
|
|
195
|
+
if (!bySubpath.has(subpath))
|
|
196
|
+
bySubpath.set(subpath, []);
|
|
197
|
+
bySubpath.get(subpath).push(spec);
|
|
198
|
+
}
|
|
199
|
+
return Array.from(bySubpath.entries()).map(([subpath, specs]) => t.importDeclaration(specs, t.stringLiteral(`$rime/modules/${pkgName}/${subpath}`)));
|
|
200
|
+
}
|
|
201
|
+
/** `export { x } from '$rime/modules'` — what `tsc`/`svelte-package` actually emits in `.d.ts`
|
|
202
|
+
* output (a real end-to-end run caught this; `import`+`export` pairs aren't the only shape).
|
|
203
|
+
* Same grouping as `splitBarrelImport`, just keyed off `.local` (the name as it exists in the
|
|
204
|
+
* source module) instead of `.imported` — `ExportSpecifier` doesn't have that field. */
|
|
205
|
+
function splitBarrelReExport(node, fullPath, pkgName, nameOwner) {
|
|
206
|
+
const bySubpath = new Map();
|
|
207
|
+
for (const spec of node.specifiers) {
|
|
208
|
+
if (!t.isExportSpecifier(spec)) {
|
|
209
|
+
throw new Error(`$rime/modules: ${fullPath} re-exports the barrel with a default/namespace specifier — only named exports are supported`);
|
|
210
|
+
}
|
|
211
|
+
const localName = spec.local.name;
|
|
212
|
+
const subpath = nameOwner.get(localName);
|
|
213
|
+
if (!subpath) {
|
|
214
|
+
throw new Error(`$rime/modules: '${localName}' re-exported in ${fullPath} but not exported by any split`);
|
|
215
|
+
}
|
|
216
|
+
if (!bySubpath.has(subpath))
|
|
217
|
+
bySubpath.set(subpath, []);
|
|
218
|
+
bySubpath.get(subpath).push(spec);
|
|
219
|
+
}
|
|
220
|
+
return Array.from(bySubpath.entries()).map(([subpath, specs]) => t.exportNamedDeclaration(null, specs, t.stringLiteral(`$rime/modules/${pkgName}/${subpath}`)));
|
|
221
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { logger } from '../../logger/index.server.js';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
3
4
|
import { program } from 'commander';
|
|
4
5
|
import { existsSync, writeFileSync } from 'fs';
|
|
5
6
|
import { envProduction } from './build/templates.js';
|
|
@@ -44,6 +45,47 @@ program
|
|
|
44
45
|
process.exitCode = 1;
|
|
45
46
|
}
|
|
46
47
|
});
|
|
48
|
+
program
|
|
49
|
+
.command('package')
|
|
50
|
+
.description("Builds a rime plugin/field package for publish: svelte-kit sync, svelte-package, then " +
|
|
51
|
+
'generate-manifest — makes any $rime/modules splits it exports consumable by anyone who ' +
|
|
52
|
+
'installs it. Equivalent to chaining those three yourself.')
|
|
53
|
+
.action(() => {
|
|
54
|
+
const sync = spawnSync('./node_modules/.bin/svelte-kit', ['sync'], { stdio: 'inherit' });
|
|
55
|
+
if (sync.status !== 0) {
|
|
56
|
+
process.exitCode = sync.status ?? 1;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const build = spawnSync('./node_modules/.bin/svelte-package', [], { stdio: 'inherit' });
|
|
60
|
+
if (build.status !== 0) {
|
|
61
|
+
process.exitCode = build.status ?? 1;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
import('./generate-manifest/index.server.js').then(({ generateManifest }) => {
|
|
65
|
+
try {
|
|
66
|
+
generateManifest();
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
logger.error(error.message);
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
program
|
|
75
|
+
.command('generate-manifest')
|
|
76
|
+
.description("Run after svelte-package, before publish — makes this package's own $rime/modules " +
|
|
77
|
+
'splits consumable by anyone who installs it (rewrites the barrel into qualified ' +
|
|
78
|
+
'imports in dist/, writes dist/.rime-modules.json + .d.ts).')
|
|
79
|
+
.action(async () => {
|
|
80
|
+
const generateManifest = await import('./generate-manifest/index.server.js').then((m) => m.generateManifest);
|
|
81
|
+
try {
|
|
82
|
+
generateManifest();
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
logger.error(error.message);
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
47
89
|
program
|
|
48
90
|
.command('env')
|
|
49
91
|
.option('-f, --force', 'Force generation, overwrite current', false)
|
|
@@ -54,13 +54,13 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
|
|
|
54
54
|
}
|
|
55
55
|
function setConfig(name) {
|
|
56
56
|
const configDirPath = path.resolve(root, 'src/lib', INPUT_DIR);
|
|
57
|
-
const configPath = path.join(configDirPath, 'rime.config.ts');
|
|
57
|
+
const configPath = path.join(configDirPath, 'rime.config.server.ts');
|
|
58
58
|
if (!existsSync(configPath)) {
|
|
59
59
|
if (!existsSync(configDirPath)) {
|
|
60
60
|
mkdirSync(configDirPath);
|
|
61
61
|
}
|
|
62
62
|
writeFileSync(configPath, templates.defaultConfig(name.toString()));
|
|
63
|
-
logger.info(`[✓] Config created at ${configDirPath}/rime.config.ts`);
|
|
63
|
+
logger.info(`[✓] Config created at ${configDirPath}/rime.config.server.ts`);
|
|
64
64
|
}
|
|
65
65
|
else {
|
|
66
66
|
logger.info('[✓] Config already exists (skip)');
|
|
@@ -83,7 +83,14 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
|
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
let gitignoreContent = readFileSync(gitignorePath, 'utf-8');
|
|
86
|
-
const updates = [
|
|
86
|
+
const updates = [
|
|
87
|
+
'\\.cache',
|
|
88
|
+
'/logs',
|
|
89
|
+
'/db',
|
|
90
|
+
'\\+rime.generated',
|
|
91
|
+
'src/app.generated.d.ts',
|
|
92
|
+
'src/rime.generated.d.ts'
|
|
93
|
+
];
|
|
87
94
|
if (!gitignoreContent.includes('# rime'))
|
|
88
95
|
gitignoreContent += '\n# rime';
|
|
89
96
|
for (const line of updates) {
|
|
@@ -185,7 +192,7 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
|
|
|
185
192
|
mkdirSync(srcDir, { recursive: true });
|
|
186
193
|
}
|
|
187
194
|
// Create hooks.server.ts with template content
|
|
188
|
-
writeFileSync(hooksPath, templates.hooks, 'utf-8');
|
|
195
|
+
writeFileSync(hooksPath, templates.hooks(), 'utf-8');
|
|
189
196
|
logger.info('[✓] hooks.server.ts created');
|
|
190
197
|
}
|
|
191
198
|
else {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const env: () => string;
|
|
2
2
|
export declare const defaultConfig: (name: string) => string;
|
|
3
3
|
export declare const drizzleConfig: (name: string) => string;
|
|
4
|
-
export declare const hooks
|
|
4
|
+
export declare const hooks: () => string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomId } from '../../../../util/random.js';
|
|
2
|
-
import { OUTPUT_DIR } from '../../constants.js';
|
|
2
|
+
import { configImportPaths, OUTPUT_DIR } from '../../constants.js';
|
|
3
3
|
const PACKAGE = 'rimecms';
|
|
4
4
|
export const env = () => `BETTER_AUTH_SECRET=${randomId(32)}
|
|
5
5
|
PUBLIC_RIME_URL=http://localhost:5173
|
|
@@ -51,9 +51,11 @@ export const config: Config = {
|
|
|
51
51
|
|
|
52
52
|
export default defineConfig(config);
|
|
53
53
|
`;
|
|
54
|
-
|
|
54
|
+
// Regenerated fresh by rime init every time (setHooks() only skips if the file already
|
|
55
|
+
// exists, and `rime clear`/useConfig.js delete it first) — safe to make mode-aware.
|
|
56
|
+
export const hooks = () => `import { sequence } from '@sveltejs/kit/hooks';
|
|
55
57
|
import { handlers } from '${PACKAGE}/server';
|
|
56
|
-
import config from '
|
|
58
|
+
import config from '.${configImportPaths().server.replace('$lib', '/lib')}';
|
|
57
59
|
|
|
58
60
|
export const handle = sequence(...(await handlers(config)));
|
|
59
61
|
`;
|
|
@@ -9,22 +9,24 @@ const packageManagersMap = {
|
|
|
9
9
|
npm: 'package-lock.json',
|
|
10
10
|
deno: 'deno.lock'
|
|
11
11
|
};
|
|
12
|
+
const deps = ['drizzle-orm', '@libsql/client', '@lucide/svelte', 'sharp'];
|
|
13
|
+
const devDeps = ['@sveltejs/adapter-node', 'drizzle-kit'];
|
|
12
14
|
const packageManagerConfigs = {
|
|
13
15
|
yarn: {
|
|
14
16
|
command: 'echo "yarn is not supported, please use pnpm or npm" && exit 1'
|
|
15
17
|
},
|
|
16
18
|
pnpm: {
|
|
17
|
-
command:
|
|
19
|
+
command: `pnpm add -D ${devDeps.join(' ')} && pnpm add ${deps.join(' ')}`,
|
|
18
20
|
preInstall: configurePnpm,
|
|
19
21
|
postInstall: () => {
|
|
20
22
|
execSync('pnpm rebuild');
|
|
21
23
|
}
|
|
22
24
|
},
|
|
23
25
|
bun: {
|
|
24
|
-
command:
|
|
26
|
+
command: `bun add -D ${devDeps.join(' ')} && bun add ${deps.join(' ')}`
|
|
25
27
|
},
|
|
26
28
|
npm: {
|
|
27
|
-
command:
|
|
29
|
+
command: `npm install -D ${devDeps.join(' ')} && npm install ${deps.join(' ')}`
|
|
28
30
|
},
|
|
29
31
|
deno: {
|
|
30
32
|
command: 'echo "deno is not supported, please use pnpm or npm" && exit 1'
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
export declare const OUTPUT_DIR = "+rime.generated";
|
|
2
2
|
export declare const INPUT_DIR = "+rime";
|
|
3
|
+
/** Where the panel/live route templates (common.server.ts) should import the client config
|
|
4
|
+
* from, and where the dev-server watcher/hot-reload should read the server config from —
|
|
5
|
+
* one source of truth instead of hardcoding the path at each call site. */
|
|
6
|
+
export declare function configImportPaths(): {
|
|
7
|
+
client: string;
|
|
8
|
+
server: string;
|
|
9
|
+
};
|
|
10
|
+
/** Same idea as configImportPaths — one source of truth for where the generated schema
|
|
11
|
+
* lives instead of hardcoding the path at each call site. */
|
|
12
|
+
export declare function schemaPath(root?: string): string;
|
|
3
13
|
/**
|
|
4
14
|
* True when `metaUrl` (pass the caller's own `import.meta.url` — this can't be one shared
|
|
5
15
|
* value, it depends on which file is asking) is running from inside an installed `rimecms`
|
|
@@ -1,5 +1,20 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
1
2
|
export const OUTPUT_DIR = '+rime.generated';
|
|
2
3
|
export const INPUT_DIR = '+rime';
|
|
4
|
+
/** Where the panel/live route templates (common.server.ts) should import the client config
|
|
5
|
+
* from, and where the dev-server watcher/hot-reload should read the server config from —
|
|
6
|
+
* one source of truth instead of hardcoding the path at each call site. */
|
|
7
|
+
export function configImportPaths() {
|
|
8
|
+
return {
|
|
9
|
+
client: `$lib/${OUTPUT_DIR}/rime.config.js`,
|
|
10
|
+
server: `$lib/${OUTPUT_DIR}/rime.config.server.js`
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/** Same idea as configImportPaths — one source of truth for where the generated schema
|
|
14
|
+
* lives instead of hardcoding the path at each call site. */
|
|
15
|
+
export function schemaPath(root = process.cwd()) {
|
|
16
|
+
return path.resolve(root, 'src/lib', OUTPUT_DIR, 'schema.server.ts');
|
|
17
|
+
}
|
|
3
18
|
/**
|
|
4
19
|
* True when `metaUrl` (pass the caller's own `import.meta.url` — this can't be one shared
|
|
5
20
|
* value, it depends on which file is asking) is running from inside an installed `rimecms`
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { PACKAGE_NAME } from '../../../constant.server.js';
|
|
2
|
-
import {
|
|
3
|
-
// Trick to prevent svelte-kit to replace $lib with resolved imports path
|
|
4
|
-
// same thing for PACKAGE_NAME
|
|
5
|
-
const DOLLAR_LIB = '$lib';
|
|
2
|
+
import { configImportPaths } from '../../constants.js';
|
|
6
3
|
/**
|
|
7
4
|
* Main base layout
|
|
8
5
|
* /+layout.server.ts
|
|
@@ -149,8 +146,8 @@ export const load = (event: ServerLoadEvent) => event.locals.routes.panel.load.r
|
|
|
149
146
|
const panelLayout = () => `
|
|
150
147
|
<script>
|
|
151
148
|
import { Panel } from '${PACKAGE_NAME}/panel';
|
|
152
|
-
import config from '${
|
|
153
|
-
|
|
149
|
+
import config from '${configImportPaths().client}';
|
|
150
|
+
|
|
154
151
|
const { children, data } = $props();
|
|
155
152
|
|
|
156
153
|
const user = $derived.by(() => {
|
|
@@ -199,7 +196,7 @@ export const load = (event: ServerLoadEvent) => event.locals.routes.panel.load.d
|
|
|
199
196
|
const livePage = () => `
|
|
200
197
|
<script>
|
|
201
198
|
import { Live } from '${PACKAGE_NAME}/panel';
|
|
202
|
-
import config from '${
|
|
199
|
+
import config from '${configImportPaths().client}';
|
|
203
200
|
|
|
204
201
|
const { data } = $props();
|
|
205
202
|
</script>
|
|
@@ -30,15 +30,36 @@ function generateRoutes(config) {
|
|
|
30
30
|
ensureDir(rimeRoutes);
|
|
31
31
|
ensureDir(panelRoute);
|
|
32
32
|
ensureDir(paramsDir);
|
|
33
|
-
// 3. Process common routes (now includes the matcher-disambiguated [slug=collection|area] routes)
|
|
33
|
+
// 3. Process common routes (now includes the matcher-disambiguated [slug=collection|area] routes) —
|
|
34
|
+
// skip the [slug=collection]/[slug=area] routes and their matchers when there are no collections/areas
|
|
35
|
+
// configured, rather than generating a matcher that can never match anything.
|
|
36
|
+
const hasCollections = (config.collections || []).length > 0;
|
|
37
|
+
const hasAreas = (config.areas || []).length > 0;
|
|
34
38
|
for (const [pattern, files] of Object.entries(commonRoutes)) {
|
|
39
|
+
if (!hasCollections && pattern.includes('[slug=collection]'))
|
|
40
|
+
continue;
|
|
41
|
+
if (!hasAreas && pattern.includes('[slug=area]'))
|
|
42
|
+
continue;
|
|
35
43
|
for (const [fileType, templateFn] of Object.entries(files)) {
|
|
36
44
|
writeRouteFile(rootRoutes, pattern, fileType, templateFn());
|
|
37
45
|
}
|
|
38
46
|
}
|
|
39
|
-
// 4. Write the [slug=collection]/[slug=area] param matchers
|
|
40
|
-
|
|
41
|
-
|
|
47
|
+
// 4. Write the [slug=collection]/[slug=area] param matchers, only when needed — and remove any
|
|
48
|
+
// stale matcher left over from a previous config that did have collections/areas.
|
|
49
|
+
const collectionMatcherPath = path.join(paramsDir, 'collection.ts');
|
|
50
|
+
if (hasCollections) {
|
|
51
|
+
fs.writeFileSync(collectionMatcherPath, paramMatcher((config.collections || []).map((c) => c.kebab)));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
fs.rmSync(collectionMatcherPath, { force: true });
|
|
55
|
+
}
|
|
56
|
+
const areaMatcherPath = path.join(paramsDir, 'area.ts');
|
|
57
|
+
if (hasAreas) {
|
|
58
|
+
fs.writeFileSync(areaMatcherPath, paramMatcher((config.areas || []).map((a) => a.kebab)));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
fs.rmSync(areaMatcherPath, { force: true });
|
|
62
|
+
}
|
|
42
63
|
// 5. Handle custom routes from config
|
|
43
64
|
const customRoutes = config.panel?.routes;
|
|
44
65
|
if (customRoutes) {
|