storyloco 2.0.0 → 2.0.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/package.json +1 -1
- package/packages/vite/dist/schema/plugin.js +126 -106
package/package.json
CHANGED
|
@@ -100,119 +100,139 @@ async function format_code(filename, content) {
|
|
|
100
100
|
*/
|
|
101
101
|
export default function schema({ output_path, interval_ms, storyblok_personal_access_token, storyblok_space_id, filename, } = {}) {
|
|
102
102
|
let interval = null;
|
|
103
|
+
let mode = "development";
|
|
104
|
+
let build_run = null;
|
|
105
|
+
// ensure output_path does not end with a slash
|
|
106
|
+
const out_dir = output_path?.replace(/\/?$/, "") ?? "src/lib";
|
|
107
|
+
const schema_ts_file = path.join(out_dir, filename ?? "components.schema.ts");
|
|
108
|
+
async function generate() {
|
|
109
|
+
const env = loadEnv(mode, process.cwd(), "");
|
|
110
|
+
const token = storyblok_personal_access_token ?? env.STORYBLOK_PERSONAL_ACCESS_TOKEN;
|
|
111
|
+
const space_id = storyblok_space_id ?? env.STORYBLOK_SPACE_ID;
|
|
112
|
+
// Skip rather than throw: plenty of builds (CI without secrets, contributors
|
|
113
|
+
// without Storyblok access) have no business failing over generated types.
|
|
114
|
+
if (!token || !space_id) {
|
|
115
|
+
logger.warn("STORYBLOK_PERSONAL_ACCESS_TOKEN and STORYBLOK_SPACE_ID are both required to generate component types — skipping");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const x = await exec();
|
|
119
|
+
const components_file = path.join(".svelte-kit/storyblok/components", space_id, "components.json");
|
|
120
|
+
try {
|
|
121
|
+
// Check if components file exists
|
|
122
|
+
let existing_components = "";
|
|
123
|
+
try {
|
|
124
|
+
await access(components_file, constants.F_OK);
|
|
125
|
+
existing_components = await readFile(components_file, "utf-8");
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// No existing file, that's fine
|
|
129
|
+
}
|
|
130
|
+
// Fetch the new components
|
|
131
|
+
const url = `https://mapi.storyblok.com/v1/spaces/${space_id}/components`;
|
|
132
|
+
const headers = new Headers({ Authorization: token });
|
|
133
|
+
const response = await fetch(url, { headers });
|
|
134
|
+
const { components: new_components } = await response.json();
|
|
135
|
+
// Make the file writable
|
|
136
|
+
await $ `chmod u+w ${components_file}`.quiet().nothrow();
|
|
137
|
+
// Nothing to do only if the schema is unchanged *and* the output is still there —
|
|
138
|
+
// checking the cache alone means a deleted output file never comes back.
|
|
139
|
+
const output_exists = await access(schema_ts_file, constants.F_OK).then(() => true, () => false);
|
|
140
|
+
if (output_exists &&
|
|
141
|
+
existing_components &&
|
|
142
|
+
JSON.stringify(JSON.parse(existing_components)) === JSON.stringify(new_components))
|
|
143
|
+
return;
|
|
144
|
+
// Create the components directory if it doesn't exist
|
|
145
|
+
await mkdir(path.dirname(components_file), { recursive: true });
|
|
146
|
+
// Write the data to the components file
|
|
147
|
+
await writeFile(components_file, JSON.stringify(new_components, null, 2));
|
|
148
|
+
const here = (str) => process.env.SCHEMA_DEV
|
|
149
|
+
? path.join("src/lib/schema", str)
|
|
150
|
+
: path.join("node_modules/storyloco/packages/vite/dist/schema", str);
|
|
151
|
+
// Generate types
|
|
152
|
+
logger.start("Generating TypeScript definitions...");
|
|
153
|
+
await $ `chmod u+w ${schema_ts_file}`.quiet().nothrow();
|
|
154
|
+
await $ `${x} storyblok@4.6.6 ts generate -s ${space_id} -p .svelte-kit/storyblok --compiler-options ${here("compiler_options.js")} --custom-fields-parser ${here("custom_fields_parser.js")} --strict`.quiet();
|
|
155
|
+
logger.succeed("TypeScript definitions generated.");
|
|
156
|
+
// Get generated files
|
|
157
|
+
let component_types;
|
|
158
|
+
let general;
|
|
159
|
+
try {
|
|
160
|
+
component_types = await readFile(`.svelte-kit/storyblok/types/${space_id}/storyblok-components.d.ts`, "utf-8");
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
logger.warn("Component types file not found, skipping type generation");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
general = await readFile(".svelte-kit/storyblok/types/storyblok.d.ts", "utf-8");
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
logger.warn("General types file not found, skipping type generation");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const general_replaced = general.replace(`// This file was generated by the Storyblok CLI.
|
|
174
|
+
// DO NOT MODIFY THIS FILE BY HAND.
|
|
175
|
+
import type { ISbStoryData } from '@storyblok/js';`, "");
|
|
176
|
+
// determine how many folders are in output_path
|
|
177
|
+
const output_path_parts = out_dir.split("/");
|
|
178
|
+
const import_path = output_path_parts.map(() => `..`).join("/");
|
|
179
|
+
const header = `
|
|
180
|
+
/**
|
|
181
|
+
* AUTO-GENERATED FILE. DO NOT EDIT.
|
|
182
|
+
* Generated by ${name} plugin on ${new Date().toISOString()}.
|
|
183
|
+
* Any changes will be overwritten.
|
|
184
|
+
*/
|
|
185
|
+
|
|
186
|
+
import type { StoryblokCustomPlugins } from "${import_path}/vite.config.js"
|
|
187
|
+
import type { SbBlokData } from "@storyblok/svelte"
|
|
188
|
+
|
|
189
|
+
export type Blok<T> = SbBlokData & T
|
|
190
|
+
`;
|
|
191
|
+
let content = component_types.replace(`// This file was generated by the storyblok CLI.
|
|
192
|
+
// DO NOT MODIFY THIS FILE BY HAND.`, header);
|
|
193
|
+
content = content.replace(/^.*\sfrom\s+'\.{2}\/storyblok\.d\.ts';\s*$/gm, "");
|
|
194
|
+
content = content + general_replaced;
|
|
195
|
+
content = content.replace(/\["StoryblokCustomPlugins/g, 'StoryblokCustomPlugins["');
|
|
196
|
+
// Try and use unknown instead of any
|
|
197
|
+
content = content.replace(/Record<string, any>/g, "Record<string, unknown>");
|
|
198
|
+
content = content.replace(/any\[]/g, "unknown[]");
|
|
199
|
+
// Make _uid and component optional (helps us reuse Storyblok types for non-storyblok components)
|
|
200
|
+
content = content.replace(/^([a-zA-Z0-9_]+):/gm, "$1?:");
|
|
201
|
+
const formatted = await format_code(path.basename(schema_ts_file), content);
|
|
202
|
+
// Write the final file
|
|
203
|
+
logger.start("Finalising schema file...");
|
|
204
|
+
await writeFile(schema_ts_file, formatted);
|
|
205
|
+
// Lock the file
|
|
206
|
+
await $ `chmod a-w ${schema_ts_file}`.quiet();
|
|
207
|
+
logger.succeed(`Storyblok component types generated successfully to ${Logger.color.magenta(schema_ts_file)}`);
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
const message = err instanceof Error ? err.message : "An unknown error occurred.";
|
|
211
|
+
logger.fail(message);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
103
214
|
return {
|
|
104
215
|
name,
|
|
216
|
+
configResolved(config) {
|
|
217
|
+
mode = config.mode;
|
|
218
|
+
},
|
|
219
|
+
/**
|
|
220
|
+
* Types are needed by anything that type-checks a build — CI especially, where
|
|
221
|
+
* the generated file is gitignored and no dev server ever runs. Vite builds each
|
|
222
|
+
* environment separately, so this hook fires more than once; generate once and
|
|
223
|
+
* let the later calls await the same run.
|
|
224
|
+
*/
|
|
225
|
+
async buildStart() {
|
|
226
|
+
build_run ??= generate();
|
|
227
|
+
await build_run;
|
|
228
|
+
},
|
|
105
229
|
async configureServer(server) {
|
|
106
|
-
|
|
107
|
-
const x = await exec();
|
|
230
|
+
mode = server.config.mode;
|
|
108
231
|
interval_ms = interval_ms ?? 60000; // 1 minute
|
|
109
|
-
// ensure output_path does not end with a slash
|
|
110
|
-
output_path = output_path?.replace(/\/?$/, "") ?? "src/lib";
|
|
111
|
-
storyblok_personal_access_token =
|
|
112
|
-
storyblok_personal_access_token ?? env.STORYBLOK_PERSONAL_ACCESS_TOKEN;
|
|
113
|
-
storyblok_space_id = storyblok_space_id ?? env.STORYBLOK_SPACE_ID;
|
|
114
|
-
const components_file = path.join(".svelte-kit/storyblok/components", storyblok_space_id, "components.json");
|
|
115
|
-
const schema_ts_file = path.join(output_path, filename ?? "components.schema.ts");
|
|
116
232
|
const seconds = interval_ms / 1000;
|
|
117
233
|
logger.info(`Regenerating Storyblok component types every ${seconds} ${seconds === 1 ? "second" : "seconds"}`);
|
|
118
|
-
generate();
|
|
234
|
+
await generate();
|
|
119
235
|
interval = setInterval(generate, interval_ms);
|
|
120
|
-
async function generate() {
|
|
121
|
-
if (!storyblok_personal_access_token)
|
|
122
|
-
throw new Error("Storyblok personal access token is required");
|
|
123
|
-
if (!storyblok_space_id)
|
|
124
|
-
throw new Error("Storyblok space ID is required");
|
|
125
|
-
try {
|
|
126
|
-
// Check if components file exists
|
|
127
|
-
let existing_components = "";
|
|
128
|
-
try {
|
|
129
|
-
await access(components_file, constants.F_OK);
|
|
130
|
-
existing_components = await readFile(components_file, "utf-8");
|
|
131
|
-
}
|
|
132
|
-
catch {
|
|
133
|
-
// No existing file, that's fine
|
|
134
|
-
}
|
|
135
|
-
// Fetch the new components
|
|
136
|
-
const url = `https://mapi.storyblok.com/v1/spaces/${storyblok_space_id}/components`;
|
|
137
|
-
const headers = new Headers({ Authorization: storyblok_personal_access_token });
|
|
138
|
-
const response = await fetch(url, { headers });
|
|
139
|
-
const { components: new_components } = await response.json();
|
|
140
|
-
// Make the file writable
|
|
141
|
-
await $ `chmod u+w ${components_file}`.quiet().nothrow();
|
|
142
|
-
// Compare existing and new components
|
|
143
|
-
if (existing_components &&
|
|
144
|
-
JSON.stringify(JSON.parse(existing_components)) === JSON.stringify(new_components))
|
|
145
|
-
return;
|
|
146
|
-
// Create the components directory if it doesn't exist
|
|
147
|
-
await mkdir(path.dirname(components_file), { recursive: true });
|
|
148
|
-
// Write the data to the components file
|
|
149
|
-
await writeFile(components_file, JSON.stringify(new_components, null, 2));
|
|
150
|
-
const here = (str) => process.env.SCHEMA_DEV
|
|
151
|
-
? path.join("src/lib/schema", str)
|
|
152
|
-
: path.join("node_modules/storyloco/packages/vite/dist/schema", str);
|
|
153
|
-
// Generate types
|
|
154
|
-
logger.start("Generating TypeScript definitions...");
|
|
155
|
-
await $ `chmod u+w ${schema_ts_file}`.quiet().nothrow();
|
|
156
|
-
await $ `${x} storyblok@4.6.6 ts generate -s ${storyblok_space_id} -p .svelte-kit/storyblok --compiler-options ${here("compiler_options.js")} --custom-fields-parser ${here("custom_fields_parser.js")} --strict`.quiet();
|
|
157
|
-
logger.succeed("TypeScript definitions generated.");
|
|
158
|
-
// Get generated files
|
|
159
|
-
let component_types;
|
|
160
|
-
let general;
|
|
161
|
-
try {
|
|
162
|
-
component_types = await readFile(`.svelte-kit/storyblok/types/${storyblok_space_id}/storyblok-components.d.ts`, "utf-8");
|
|
163
|
-
}
|
|
164
|
-
catch (error) {
|
|
165
|
-
logger.warn("Component types file not found, skipping type generation");
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
try {
|
|
169
|
-
general = await readFile(".svelte-kit/storyblok/types/storyblok.d.ts", "utf-8");
|
|
170
|
-
}
|
|
171
|
-
catch (error) {
|
|
172
|
-
logger.warn("General types file not found, skipping type generation");
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
const general_replaced = general.replace(`// This file was generated by the Storyblok CLI.
|
|
176
|
-
// DO NOT MODIFY THIS FILE BY HAND.
|
|
177
|
-
import type { ISbStoryData } from '@storyblok/js';`, "");
|
|
178
|
-
// determine how many folders are in output_path
|
|
179
|
-
const output_path_parts = output_path?.split("/") ?? [];
|
|
180
|
-
const import_path = output_path_parts.map(() => `..`).join("/");
|
|
181
|
-
const header = `
|
|
182
|
-
/**
|
|
183
|
-
* AUTO-GENERATED FILE. DO NOT EDIT.
|
|
184
|
-
* Generated by ${name} plugin on ${new Date().toISOString()}.
|
|
185
|
-
* Any changes will be overwritten.
|
|
186
|
-
*/
|
|
187
|
-
|
|
188
|
-
import type { StoryblokCustomPlugins } from "${import_path}/vite.config.js"
|
|
189
|
-
import type { SbBlokData } from "@storyblok/svelte"
|
|
190
|
-
|
|
191
|
-
export type Blok<T> = SbBlokData & T
|
|
192
|
-
`;
|
|
193
|
-
let content = component_types.replace(`// This file was generated by the storyblok CLI.
|
|
194
|
-
// DO NOT MODIFY THIS FILE BY HAND.`, header);
|
|
195
|
-
content = content.replace(/^.*\sfrom\s+'\.{2}\/storyblok\.d\.ts';\s*$/gm, "");
|
|
196
|
-
content = content + general_replaced;
|
|
197
|
-
content = content.replace(/\["StoryblokCustomPlugins/g, 'StoryblokCustomPlugins["');
|
|
198
|
-
// Try and use unknown instead of any
|
|
199
|
-
content = content.replace(/Record<string, any>/g, "Record<string, unknown>");
|
|
200
|
-
content = content.replace(/any\[]/g, "unknown[]");
|
|
201
|
-
// Make _uid and component optional (helps us reuse Storyblok types for non-storyblok components)
|
|
202
|
-
content = content.replace(/^([a-zA-Z0-9_]+):/gm, "$1?:");
|
|
203
|
-
const formatted = await format_code(path.basename(schema_ts_file), content);
|
|
204
|
-
// Write the final file
|
|
205
|
-
logger.start("Finalising schema file...");
|
|
206
|
-
await writeFile(schema_ts_file, formatted);
|
|
207
|
-
// Lock the file
|
|
208
|
-
await $ `chmod a-w ${schema_ts_file}`.quiet();
|
|
209
|
-
logger.succeed(`Storyblok component types generated successfully to ${Logger.color.magenta(schema_ts_file)}`);
|
|
210
|
-
}
|
|
211
|
-
catch (err) {
|
|
212
|
-
const message = err instanceof Error ? err.message : "An unknown error occurred.";
|
|
213
|
-
logger.fail(message);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
236
|
},
|
|
217
237
|
closeBundle() {
|
|
218
238
|
if (interval)
|