@fluid-app/fluid-cli-theme-dev 0.1.15 → 0.1.17
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/.turbo/turbo-build.log +7 -5
- package/.turbo/turbo-typecheck.log +4 -0
- package/dist/index.mjs +436 -56
- package/dist/index.mjs.map +1 -1
- package/jest.config.cjs +21 -0
- package/jest.mocks/fluid-cli.ts +33 -0
- package/package.json +10 -5
- package/src/__tests__/plugin-state.test.ts +186 -0
- package/src/commands/dev.ts +36 -16
- package/src/commands/lint.ts +175 -0
- package/src/commands/navigate.ts +2 -4
- package/src/commands/theme.ts +3 -1
- package/src/plugin-state.ts +156 -11
package/src/plugin-state.ts
CHANGED
|
@@ -1,26 +1,171 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
1
2
|
import { readConfig, updateConfig } from "@fluid-app/fluid-cli";
|
|
2
3
|
|
|
4
|
+
export interface DevThemeRef {
|
|
5
|
+
id: number;
|
|
6
|
+
name: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
interface ThemeDevState {
|
|
10
|
+
/**
|
|
11
|
+
* Dev themes keyed per project, so `theme dev` in one working copy never
|
|
12
|
+
* reuses (and clobbers) another project's sandbox theme. See `devThemeKey`.
|
|
13
|
+
* Entries are pruned once their theme directory no longer exists, so the map
|
|
14
|
+
* can't grow without bound as projects (and one-off/temp dirs) come and go.
|
|
15
|
+
*/
|
|
16
|
+
devThemes?: Record<string, DevThemeRef>;
|
|
17
|
+
/** Most recently started dev theme — `navigate`'s default target. */
|
|
18
|
+
lastDevThemeId?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Legacy single global dev theme id. Older CLI versions stored one dev theme
|
|
21
|
+
* here regardless of project. Read once for migration (see `getDevTheme`),
|
|
22
|
+
* then dropped in favour of `devThemes`.
|
|
23
|
+
*/
|
|
4
24
|
devThemeId?: number;
|
|
25
|
+
/** Legacy companion to `devThemeId`. */
|
|
5
26
|
devThemeName?: string;
|
|
6
27
|
}
|
|
7
28
|
|
|
8
29
|
const PLUGIN_KEY = "theme-dev";
|
|
9
30
|
|
|
10
|
-
|
|
31
|
+
function getState(): ThemeDevState {
|
|
11
32
|
const config = readConfig();
|
|
12
33
|
return (config.plugins[PLUGIN_KEY] as ThemeDevState) ?? {};
|
|
13
34
|
}
|
|
14
35
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
36
|
+
/** Extract the absolute theme root from a `company:themeRoot` key. */
|
|
37
|
+
function themeRootFromKey(key: string): string {
|
|
38
|
+
const sep = key.indexOf(":");
|
|
39
|
+
return sep === -1 ? key : key.slice(sep + 1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Set `key` to `theme`, dropping any entries whose theme directory no longer
|
|
44
|
+
* exists. Tying an entry's lifetime to its directory keeps the map bounded —
|
|
45
|
+
* abandoned/deleted projects fall out the next time `theme dev` runs anywhere.
|
|
46
|
+
*/
|
|
47
|
+
function withDevTheme(
|
|
48
|
+
existing: Record<string, DevThemeRef> | undefined,
|
|
49
|
+
key: string,
|
|
50
|
+
theme: DevThemeRef,
|
|
51
|
+
): Record<string, DevThemeRef> {
|
|
52
|
+
const next: Record<string, DevThemeRef> = {};
|
|
53
|
+
for (const [k, v] of Object.entries(existing ?? {})) {
|
|
54
|
+
if (existsSync(themeRootFromKey(k))) next[k] = v;
|
|
55
|
+
}
|
|
56
|
+
next[key] = theme;
|
|
57
|
+
return next;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Stable key identifying a dev theme's owning project: the Fluid company
|
|
62
|
+
* (subdomains are globally unique) plus the absolute theme root. Two working
|
|
63
|
+
* copies — or the same copy pulled from two companies — get distinct keys.
|
|
64
|
+
*/
|
|
65
|
+
export function devThemeKey(
|
|
66
|
+
company: string | undefined,
|
|
67
|
+
themeRoot: string,
|
|
68
|
+
): string {
|
|
69
|
+
return `${company ?? "default"}:${themeRoot}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The dev theme stored for a project key, if any. Falls back once to the legacy
|
|
74
|
+
* global `devThemeId` (older CLI versions) and adopts it for this key — clearing
|
|
75
|
+
* the legacy fields so a second project can't adopt the same theme and collide.
|
|
76
|
+
*/
|
|
77
|
+
export function getDevTheme(key: string): DevThemeRef | undefined {
|
|
78
|
+
const state = getState();
|
|
79
|
+
const existing = state.devThemes?.[key];
|
|
80
|
+
if (existing) return existing;
|
|
81
|
+
|
|
82
|
+
if (state.devThemeId) {
|
|
83
|
+
const migrated: DevThemeRef = {
|
|
84
|
+
id: state.devThemeId,
|
|
85
|
+
name: state.devThemeName ?? `Development #${state.devThemeId}`,
|
|
86
|
+
};
|
|
87
|
+
updateConfig((config) => {
|
|
88
|
+
const current = (config.plugins[PLUGIN_KEY] as ThemeDevState) ?? {};
|
|
89
|
+
const { devThemeId: _id, devThemeName: _name, ...rest } = current;
|
|
90
|
+
return {
|
|
91
|
+
...config,
|
|
92
|
+
plugins: {
|
|
93
|
+
...config.plugins,
|
|
94
|
+
[PLUGIN_KEY]: {
|
|
95
|
+
...rest,
|
|
96
|
+
devThemes: withDevTheme(rest.devThemes, key, migrated),
|
|
97
|
+
lastDevThemeId: migrated.id,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
return migrated;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Store (or refresh) the dev theme for a project key and mark it most-recent. */
|
|
109
|
+
export function setDevTheme(key: string, theme: DevThemeRef): void {
|
|
110
|
+
updateConfig((config) => {
|
|
111
|
+
const current = (config.plugins[PLUGIN_KEY] as ThemeDevState) ?? {};
|
|
112
|
+
return {
|
|
113
|
+
...config,
|
|
114
|
+
plugins: {
|
|
115
|
+
...config.plugins,
|
|
116
|
+
[PLUGIN_KEY]: {
|
|
117
|
+
...current,
|
|
118
|
+
devThemes: withDevTheme(current.devThemes, key, theme),
|
|
119
|
+
lastDevThemeId: theme.id,
|
|
120
|
+
},
|
|
23
121
|
},
|
|
24
|
-
}
|
|
25
|
-
})
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Forget a project's dev theme (it was deleted remotely or is no longer a dev theme). */
|
|
127
|
+
export function clearDevTheme(key: string): void {
|
|
128
|
+
updateConfig((config) => {
|
|
129
|
+
const current = (config.plugins[PLUGIN_KEY] as ThemeDevState) ?? {};
|
|
130
|
+
const removed = current.devThemes?.[key];
|
|
131
|
+
if (!removed) return config;
|
|
132
|
+
const { [key]: _removed, ...rest } = current.devThemes ?? {};
|
|
133
|
+
const next: ThemeDevState = { ...current, devThemes: rest };
|
|
134
|
+
// Don't leave `navigate` pointing at a theme we just forgot.
|
|
135
|
+
if (current.lastDevThemeId === removed.id) {
|
|
136
|
+
next.lastDevThemeId = undefined;
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
...config,
|
|
140
|
+
plugins: { ...config.plugins, [PLUGIN_KEY]: next },
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Mark a theme as the most recently started dev server (`navigate`'s default)
|
|
147
|
+
* without recording it as a project's dev theme — used for the `--theme`
|
|
148
|
+
* escape hatch, which may target an arbitrary (non-dev) theme.
|
|
149
|
+
*/
|
|
150
|
+
export function setLastDevThemeId(id: number): void {
|
|
151
|
+
updateConfig((config) => {
|
|
152
|
+
const current = (config.plugins[PLUGIN_KEY] as ThemeDevState) ?? {};
|
|
153
|
+
return {
|
|
154
|
+
...config,
|
|
155
|
+
plugins: {
|
|
156
|
+
...config.plugins,
|
|
157
|
+
[PLUGIN_KEY]: { ...current, lastDevThemeId: id },
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* The dev theme to target by default in `navigate` — the most recently started
|
|
165
|
+
* dev server. Falls back to the legacy global id for users who haven't yet run
|
|
166
|
+
* the per-project `theme dev`.
|
|
167
|
+
*/
|
|
168
|
+
export function getLastDevThemeId(): number | undefined {
|
|
169
|
+
const state = getState();
|
|
170
|
+
return state.lastDevThemeId ?? state.devThemeId;
|
|
26
171
|
}
|