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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storyloco",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "A collection of slick Storyblok field plugins built with Svelte 5, TypeScript, and Tailwind CSS",
5
5
  "keywords": [
6
6
  "storyblok",
@@ -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
- const env = loadEnv(server.config.mode, process.cwd(), "");
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)