vite-plugin-decap-cms 0.1.0 → 0.2.0

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 CHANGED
@@ -6,8 +6,7 @@
6
6
  ![NPM Downloads](https://img.shields.io/npm/dm/vite-plugin-decap-cms)
7
7
  ![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/ghostrider-05/vite-plugin-decap-cms)
8
8
 
9
-
10
- > [!DANGER] Unstable version
9
+ > [!WARNING]
11
10
  > This plugin has not reached a stable version, 1.0.0, and can include breaking changes following the semver specification. This plugin is open for contributions, both for code, suggestions and (missing) documentation.
12
11
 
13
12
  ## Install
@@ -53,9 +52,11 @@ export default defineConfig({
53
52
  })
54
53
  ```
55
54
 
55
+ For more options and guides, see [the documentation](https://vite-plugin-decap-cms.pages.dev)
56
+
56
57
  ## Example
57
58
 
58
- See [the documentation](https://vite-plugin-decap.pages.dev) for an example
59
+ See [the documentation CMS](https://vite-plugin-decap-cms.pages.dev/admin/index.html) for an example
59
60
 
60
61
  ## Development
61
62
 
package/dist/index.cjs CHANGED
@@ -58,6 +58,7 @@ __export(src_exports, {
58
58
  getGitData: () => getGitData
59
59
  });
60
60
  module.exports = __toCommonJS(src_exports);
61
+ var import_child_process2 = require("child_process");
61
62
  var import_yaml = require("yaml");
62
63
 
63
64
  // src/util.ts
@@ -139,35 +140,41 @@ var VitePress = class {
139
140
  createOverwriteableField("boolean", {
140
141
  name: "sidebar",
141
142
  label: "Whether to display the sidebar",
142
- default: true
143
+ default: true,
144
+ required: false
143
145
  }, overwrites == null ? void 0 : overwrites.sidebar),
144
146
  // TODO: add aside 'left' option
145
147
  createOverwriteableField("boolean", {
146
148
  name: "aside",
147
149
  label: "Whether to display the aside container",
148
- default: true
150
+ default: true,
151
+ required: false
149
152
  }, overwrites == null ? void 0 : overwrites.aside),
150
153
  // TODO: add support for [number, number] | 'deep' | false
151
154
  createOverwriteableField("number", {
152
155
  name: "outline",
153
156
  label: "The header levels in the outline",
154
- default: 2
157
+ default: 2,
158
+ required: false
155
159
  }, overwrites == null ? void 0 : overwrites.outline),
156
160
  // TODO: add support for Date
157
161
  createOverwriteableField("boolean", {
158
162
  name: "lastUpdated",
159
163
  label: "Whether to display last updated text",
160
- default: true
164
+ default: true,
165
+ required: false
161
166
  }, overwrites == null ? void 0 : overwrites.lastUpdated),
162
167
  createOverwriteableField("boolean", {
163
168
  name: "editLink",
164
169
  label: "Whether to display edit link text",
165
- default: true
170
+ default: true,
171
+ required: false
166
172
  }, overwrites == null ? void 0 : overwrites.editLink),
167
173
  createOverwriteableField("boolean", {
168
174
  name: "footer",
169
175
  label: "Whether to display footer text",
170
- default: true
176
+ default: true,
177
+ required: false
171
178
  }, overwrites == null ? void 0 : overwrites.footer),
172
179
  createOverwriteableField("string", {
173
180
  name: "pageClass",
@@ -296,71 +303,148 @@ function createConfigFile(config, command) {
296
303
  }
297
304
 
298
305
  // src/script.ts
306
+ function createCmsFunction(method, items, createParams, options) {
307
+ var _a;
308
+ const create = (params) => {
309
+ var _a2;
310
+ return `${(_a2 = options == null ? void 0 : options.base) != null ? _a2 : "CMS"}.${method}(${params})`;
311
+ };
312
+ return (items != null ? items : []).map((item) => {
313
+ const params = createParams(item);
314
+ if (!params)
315
+ return null;
316
+ else
317
+ return create(params);
318
+ }).filter(Boolean).join((_a = options == null ? void 0 : options.joinChar) != null ? _a : "\n");
319
+ }
299
320
  function createScript(options) {
300
321
  const _a = options, {
301
322
  useManualInitialization,
323
+ markdownEditorComponents,
324
+ formatters,
325
+ previewStylesheets,
302
326
  onGenerated: onGenerated,
303
327
  onInitialized
304
328
  } = _a, eventHooks = __objRest(_a, [
305
329
  "useManualInitialization",
330
+ "markdownEditorComponents",
331
+ "formatters",
332
+ "previewStylesheets",
333
+ // previewTemplates,
334
+ // widgets,
306
335
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
307
336
  "onGenerated",
308
337
  "onInitialized"
309
338
  ]);
310
- const events = Object.keys(eventHooks).map((hookName) => {
339
+ const events = createCmsFunction("registerEventListener", Object.keys(eventHooks), (hookName) => {
311
340
  const hook = eventHooks[hookName];
312
341
  if (!hook)
313
342
  return null;
314
343
  else {
315
344
  const name = hookName.slice(2)[0].toLowerCase() + hookName.slice(3);
316
- return `CMS.registerEventListener({ name: '${name}', handler: data => { function ${hook.toString()}; ${hookName}({ app: CMS, ...data }) } })`;
345
+ return `{ name: '${name}', handler: data => { function ${hook.toString()}; ${hookName}({ app: CMS, ...data }) } }`;
317
346
  }
318
- }).join("\n");
347
+ });
348
+ const customFormatters = createCmsFunction("registerCustomFormat", formatters, ({ name, extension, formatter }) => {
349
+ return `'${name}', '${extension}', ${formatter.toString()}`;
350
+ });
351
+ const customStyles = createCmsFunction("registerPreviewStyle", previewStylesheets, (style) => {
352
+ return typeof style === "string" ? style : `${style.style}, { raw: ${style.options.raw} }`;
353
+ });
354
+ const editorComponents = createCmsFunction("registerEditorComponent", markdownEditorComponents, (item) => {
355
+ const _a2 = item, { pattern, toPreview, toBlock, fromBlock } = _a2, component = __objRest(_a2, ["pattern", "toPreview", "toBlock", "fromBlock"]);
356
+ return `{ pattern: ${pattern}, toPreview: ${toPreview.toString()}, toBlock: ${toBlock.toString()}, fromBlock: ${fromBlock.toString()}, ...${JSON.stringify(component)}}`;
357
+ });
319
358
  return `
320
359
  <script>
321
360
  ${useManualInitialization ? "window.CMS_MANUAL_INIT = true;" : ""}
322
361
  ${onInitialized != void 0 ? `window.onload = () => { function ${onInitialized.toString()}; onInitialized({ app: CMS }) }` : ""}
362
+ ${customFormatters}
363
+ ${customStyles}
323
364
  ${events}
365
+ ${editorComponents}
324
366
  </script>`;
325
367
  }
326
368
 
327
369
  // src/files/index.ts
370
+ var defaultDecapCmsCdnVersion = "3.1.3";
371
+ var defaultNetlifyIdentityVersion = "1";
372
+ var addSlash = (path, slash = "/") => path.endsWith(slash) ? path : path + slash;
328
373
  function resolveCdnRoute(options) {
329
- const getUrl = (host = "https://unpkg.com/", version = "3.1.3") => {
330
- return `${host.endsWith("/") ? host : host + "/"}decap-cms@^${version}/dist/decap-cms.js`;
374
+ const getUrl = (host = "https://unpkg.com/", version = defaultDecapCmsCdnVersion) => {
375
+ return `${addSlash(host)}decap-cms@^${version}/dist/decap-cms.js`;
331
376
  };
332
377
  return typeof options === "boolean" ? options ? getUrl() : void 0 : typeof options === "string" ? options : options != void 0 ? getUrl(options.base, options.version) : void 0;
333
378
  }
334
- function getIndexFeatures(options) {
335
- var _a, _b;
336
- function useNetlifyIdentity(options2) {
337
- return options2.config.backend.name === "git-gateway";
338
- }
379
+ function resolveHead(head) {
380
+ return head.reduce((output, config) => {
381
+ if (typeof config === "string")
382
+ return output.concat(config);
383
+ if ("skip" in config) {
384
+ if (config.skip)
385
+ return output;
386
+ if (typeof config.head === "string")
387
+ return output.concat(config.head);
388
+ }
389
+ const item = "head" in config ? config.head : config;
390
+ let str = `<${item[0]}`;
391
+ for (const key in item[1]) {
392
+ str += ` ${key}="${item[1][key]}"`;
393
+ }
394
+ str += item[0] === "meta" ? "/>" : ">";
395
+ if (item[2] == void 0)
396
+ return output.concat(str);
397
+ str += item[2] + `</${item[0]}>`;
398
+ return output.concat(str);
399
+ }, []);
400
+ }
401
+ function getIndexFeatures(config, loadOptions) {
402
+ var _a;
403
+ const configRoute = config.dir ? addSlash(config.dir) + "config.yml" : void 0;
339
404
  return {
340
- cdn_route: resolveCdnRoute(options.load == void 0 || options.load.method === "cdn" ? (_b = (_a = options.load) == null ? void 0 : _a.options) != null ? _b : true : void 0),
341
- custom_logo: "logoUrl" in options.config ? options.config.logoUrl != void 0 : "logo_url" in options.config ? options.config.logo_url != void 0 : false,
342
- netlify_identity: useNetlifyIdentity(options),
343
- config_route: options.config.dir ? options.config.dir + (!options.config.dir.endsWith("/") ? "/" : "") + "config.yml" : void 0
405
+ cdn_route: resolveCdnRoute(loadOptions == void 0 || loadOptions.method === "cdn" ? (_a = loadOptions == null ? void 0 : loadOptions.options) != null ? _a : true : void 0),
406
+ custom_logo: "logoUrl" in config ? config.logoUrl != void 0 : "logo_url" in config ? config.logo_url != void 0 : false,
407
+ head: (options) => {
408
+ var _a2, _b, _c;
409
+ return resolveHead([
410
+ ["meta", { charset: "utf-8" }],
411
+ ["meta", { name: "viewport", content: "width=device-width, initial-scale=1.0" }],
412
+ ["meta", { name: "robots", content: "noindex" }],
413
+ ...(_a2 = options == null ? void 0 : options.head) != null ? _a2 : [],
414
+ ["title", {}, (_b = options == null ? void 0 : options.title) != null ? _b : "Content Manager"],
415
+ {
416
+ head: ["link", { rel: "favicon", ref: (_c = options == null ? void 0 : options.icon) != null ? _c : "" }],
417
+ skip: (options == null ? void 0 : options.icon) == void 0
418
+ },
419
+ {
420
+ head: ["script", { src: `https://identity.netlify.com/v${defaultNetlifyIdentityVersion}/netlify-identity-widget.js` }],
421
+ skip: config.backend.name !== "git-gateway"
422
+ },
423
+ {
424
+ head: ["link", { type: "text/yaml", rel: "cms-config-url", href: configRoute }],
425
+ skip: configRoute == void 0
426
+ }
427
+ ]);
428
+ }
344
429
  };
345
430
  }
346
- function createIndexFile(_options) {
431
+ function createIndexFile(pluginOptions) {
347
432
  var _a, _b;
348
- const features = getIndexFeatures(_options);
349
- const options = _options.login;
350
- const identifyScript = '<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>';
433
+ const { config, load, login: options, script } = pluginOptions;
434
+ if (options == null ? void 0 : options.html)
435
+ return options.html;
436
+ const features = getIndexFeatures(config, load);
351
437
  return `<!DOCTYPE html>
352
438
  <html>
353
439
  <head>
354
- <meta charset="utf-8" />
355
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
356
- <meta name="robots" content="noindex" />
357
- <title>${(_a = options == null ? void 0 : options.title) != null ? _a : "Content Manager"}</title>${features.netlify_identity ? identifyScript : ""}
358
- ${features.config_route ? `<link href="${features.config_route}" type="text/yaml" rel="cms-config-url">` : ""}
359
- ${((_b = options == null ? void 0 : options.head) != null ? _b : []).join("\n" + " ".repeat(8))}
440
+ ${features.head(options).join("\n" + " ".repeat(8))}
360
441
  </head>
361
442
  <body>
362
443
  ${features.cdn_route ? `<script src="${features.cdn_route}"></script>` : ""}
363
- ${_options.script ? createScript(_options.script) : ""}
444
+ ${script ? createScript(script) : ""}
445
+ ${((_a = pluginOptions.login) == null ? void 0 : _a.additionalHtml) ? `${(_b = pluginOptions.login) == null ? void 0 : _b.additionalHtml}
446
+
447
+ <div id="nc-root"></div>` : ""}
364
448
  </body>
365
449
  </html>${features.custom_logo ? `
366
450
 
@@ -394,8 +478,24 @@ function validateLoadOptions(options) {
394
478
  if (!valid)
395
479
  throw new Error("Invalid load options for decap-cms provided");
396
480
  }
481
+ function runProxy(options) {
482
+ var _a, _b, _c, _d, _e;
483
+ const proxy = (0, import_child_process2.exec)("npx decap-server", __spreadProps(__spreadValues({}, (_a = options == null ? void 0 : options.process) != null ? _a : {}), {
484
+ env: __spreadProps(__spreadValues({}, (_c = (_b = options == null ? void 0 : options.process) == null ? void 0 : _b.env) != null ? _c : {}), {
485
+ PORT: ((_d = options == null ? void 0 : options.port) != null ? _d : 8081).toString()
486
+ })
487
+ }));
488
+ (_e = proxy.stdout) == null ? void 0 : _e.pipe(process.stdout);
489
+ proxy.on("error", (err) => {
490
+ if ("code" in err && err.code === "EADDRINUSE") {
491
+ console.log("[PROXY] Port is already used");
492
+ } else
493
+ throw err;
494
+ });
495
+ process.on("beforeExit", () => proxy.kill());
496
+ }
397
497
  async function updateConfig(options, config) {
398
- var _a, _b, _c, _d, _e, _f;
498
+ var _a, _b, _c, _d, _e, _f, _g, _h;
399
499
  validateLoadOptions(options.load);
400
500
  const loginFile = createIndexFile(options);
401
501
  const configFile = createConfigFile(options.config, config.command);
@@ -410,9 +510,12 @@ async function updateConfig(options, config) {
410
510
  ]
411
511
  }
412
512
  );
413
- await ((_d = (_c = options.script) == null ? void 0 : _c.onConfigUpdated) == null ? void 0 : _d.call(_c));
513
+ if (config.command === "serve" && configFile.local_backend !== false && ((_d = (_c = options.proxy) == null ? void 0 : _c.enabled) != null ? _d : true)) {
514
+ runProxy(options.proxy);
515
+ }
516
+ await ((_f = (_e = options.script) == null ? void 0 : _e.onConfigUpdated) == null ? void 0 : _f.call(_e));
414
517
  if (config.command === "build") {
415
- await ((_f = (_e = options.script) == null ? void 0 : _e.onGenerated) == null ? void 0 : _f.call(_e));
518
+ await ((_h = (_g = options.script) == null ? void 0 : _g.onGenerated) == null ? void 0 : _h.call(_g));
416
519
  }
417
520
  }
418
521
  function VitePluginDecapCMS(options) {
package/dist/index.d.cts CHANGED
@@ -1,13 +1,24 @@
1
- import * as vite from 'vite';
2
1
  import { Plugin } from 'vite';
3
2
  import * as yaml from 'yaml';
4
3
  import * as decap_cms_core from 'decap-cms-core';
5
- import { CmsEventListener, CMS, CmsFieldMarkdown, CmsField, CmsFieldMeta, CmsCollectionFile, CmsConfig, CmsLocalBackend, CmsBackend, CmsFieldStringOrText, CmsFieldBase, CmsCollection } from 'decap-cms-core';
4
+ import { CmsEventListener, CMS, EditorComponentOptions, Formatter, CmsFieldMarkdown, CmsField, CmsFieldMeta, CmsCollectionFile, CmsConfig, CmsLocalBackend, CmsBackend, CmsFieldStringOrText, CmsFieldBase, CmsCollection } from 'decap-cms-core';
5
+ import { ExecOptions } from 'child_process';
6
6
 
7
7
  interface CmsHookContext {
8
8
  app: CMS;
9
9
  }
10
10
  type CmsEventHookContext = CmsHookContext & Parameters<CmsEventListener['handler']>[0];
11
+ type CmsEditorComponentOptions = EditorComponentOptions & {
12
+ id: string;
13
+ label: string;
14
+ pattern: RegExp;
15
+ fields: DecapCmsField[];
16
+ };
17
+ interface CmsEditorFormatter {
18
+ name: string;
19
+ extension: string;
20
+ formatter: Formatter;
21
+ }
11
22
  type ScriptOptions = {
12
23
  [event in `on${Capitalize<CmsEventListener['name']>}`]?: (ctx: CmsEventHookContext) => Promise<void> | void;
13
24
  } & {
@@ -40,8 +51,34 @@ type ScriptOptions = {
40
51
  *
41
52
  * cms.init()
42
53
  * ```
54
+ *
55
+ * @see https://decapcms.org/docs/manual-initialization/
56
+ * @default false
43
57
  */
44
58
  useManualInitialization?: boolean;
59
+ /**
60
+ * Register custom components to use in the rich text markdown editor field
61
+ * @see https://decapcms.org/docs/custom-widgets/
62
+ */
63
+ markdownEditorComponents?: CmsEditorComponentOptions[];
64
+ /**
65
+ * Register custom file formatters.
66
+ * @see https://decapcms.org/docs/custom-formatters/
67
+ * @default []
68
+ */
69
+ formatters?: CmsEditorFormatter[];
70
+ /**
71
+ * Register custom styles to use in the CMS
72
+ * Either pass the filename of the stylesheet or with `options.raw` pass the raw styles imported.
73
+ * @see https://decapcms.org/docs/customization/
74
+ * @default []
75
+ */
76
+ previewStylesheets?: (string | {
77
+ style: string;
78
+ options: {
79
+ raw: true;
80
+ };
81
+ })[];
45
82
  };
46
83
 
47
84
  type CamelToSnakeCase<S extends string, I extends string = never> = S extends `${infer T}${infer U}` ? S extends I ? S : `${T extends Capitalize<T> ? '_' : ''}${Lowercase<T>}${CamelToSnakeCase<U>}` : S;
@@ -53,6 +90,8 @@ type KeysToCamelCase<T> = {
53
90
  [K in keyof T as CamelCase<string & K>]: T[K] extends {} ? KeysToCamelCase<T[K]> : T[K];
54
91
  };
55
92
  type PickRequired<O extends object, K extends keyof O> = Omit<O, K> & Required<Pick<O, K>>;
93
+ type EnvContextOption = boolean | 'dev' | 'prod';
94
+ type EnvDevContextOption = Exclude<EnvContextOption, 'prod'>;
56
95
  type CollectionType = 'file' | 'folder';
57
96
  type DecapCmsMarkdownFieldRenderOptions = KeysToCamelCase<Omit<CmsFieldMarkdown, 'widget' | 'default' | 'editorComponents'>>;
58
97
  type DecapCmsField = KeysToCamelCase<CmsField>;
@@ -70,7 +109,7 @@ type DecapCmsCollection<Type extends CollectionType = CollectionType> = Type ext
70
109
  }> : never;
71
110
  type DecapCmsConfig = KeysToCamelCase<Omit<CmsConfig, 'local_backend' | 'backend' | 'collections' | 'load_config_file'>> & {
72
111
  backend: {
73
- local?: boolean | 'dev' | KeysToCamelCase<CmsLocalBackend>;
112
+ local?: EnvDevContextOption | KeysToCamelCase<CmsLocalBackend>;
74
113
  /**
75
114
  * Overwrite the branch specified in `backend.branch`
76
115
  * - true: always use current branch
@@ -79,7 +118,7 @@ type DecapCmsConfig = KeysToCamelCase<Omit<CmsConfig, 'local_backend' | 'backend
79
118
  * - 'prod': ony use the current branch when building the site
80
119
  * @default false
81
120
  */
82
- useCurrentBranch?: boolean | 'dev' | 'prod';
121
+ useCurrentBranch?: EnvContextOption;
83
122
  } & KeysToCamelCase<CmsBackend>;
84
123
  collections: DecapCmsCollection[];
85
124
  /**
@@ -93,11 +132,62 @@ type CdnLinkOptions = string | {
93
132
  version?: string;
94
133
  base?: string;
95
134
  };
135
+ type HeadConfig = string | [string, Record<string, string>] | [string, Record<string, string>, string];
96
136
  interface LoginPageOptions {
137
+ /**
138
+ * The title for the CMS pages
139
+ * @default 'Content Manager'
140
+ */
97
141
  title?: string;
98
- head?: string[];
142
+ /**
143
+ * The favicon for the CMS pages
144
+ */
145
+ icon?: string;
146
+ /**
147
+ * Additional head items for the page.
148
+ * The following items are configured already:
149
+ * - title
150
+ * - viewport
151
+ * - robots
152
+ * - charset
153
+ * - favicon (if used in the config)
154
+ * - Netlify Identity script (if used in the config)
155
+ * - custom config path (if used in the config)
156
+ */
157
+ head?: HeadConfig[];
158
+ /**
159
+ * Replace the login page with your own html
160
+ */
161
+ html?: string;
162
+ /**
163
+ * Instead of replacing all html, load this next to the CMS Editor for a custom footer, nav, etc.
164
+ */
165
+ additionalHtml?: string;
166
+ /**
167
+ * The version of Netlify Identity to use
168
+ * @default '1'
169
+ */
170
+ netlifyIdentityVersion?: string;
99
171
  }
100
172
  type YmlStringifyOptions = Parameters<typeof yaml.stringify>;
173
+ interface DecapProxyOptions {
174
+ /**
175
+ * If using local backend AND Vite dev mode is running, control whether to run the decap-server proxy.
176
+ * @default true
177
+ */
178
+ enabled?: boolean;
179
+ /**
180
+ * Run the proxy on a different port.
181
+ * Does not change the local backend allowed hosts
182
+ * @default 8081
183
+ */
184
+ port?: number;
185
+ /**
186
+ * Pass any option to use in the child process
187
+ * @default undefined
188
+ */
189
+ process?: ExecOptions;
190
+ }
101
191
  interface Options {
102
192
  /**
103
193
  * How to load Decap CMS
@@ -128,6 +218,10 @@ interface Options {
128
218
  * Run custom JS to enhance the CMS
129
219
  */
130
220
  script?: ScriptOptions;
221
+ /**
222
+ * Options for the Decap server proxy
223
+ */
224
+ proxy?: DecapProxyOptions;
131
225
  /**
132
226
  * Yml stringify options for writing the config.yml file
133
227
  */
@@ -142,11 +236,11 @@ declare function getGitData(): {
142
236
  readonly commitSha: string | undefined;
143
237
  };
144
238
  declare function createField<T extends DecapCmsFieldType>(widget: T, data: Omit<DecapCmsFieldWidget<T>, 'widget'>): DecapCmsFieldWidget<T>;
145
- declare function createFolderCollection(data: DecapCmsCollection<'folder'>): KeysToCamelCase<Omit<decap_cms_core.CmsCollection, "files" | "fields">> & {
239
+ declare function createFolderCollection(data: DecapCmsCollection<'folder'>): KeysToCamelCase<Omit<decap_cms_core.CmsCollection, "fields" | "files">> & {
146
240
  fields: DecapCmsField[];
147
241
  };
148
242
  declare function createFile(data: DecapCmsCollectionFile): DecapCmsCollectionFile;
149
- declare function createFileCollection(data: DecapCmsCollection<'file'>): KeysToCamelCase<Omit<decap_cms_core.CmsCollection, "files" | "fields">> & {
243
+ declare function createFileCollection(data: DecapCmsCollection<'file'>): KeysToCamelCase<Omit<decap_cms_core.CmsCollection, "fields" | "files">> & {
150
244
  files: DecapCmsCollectionFile[];
151
245
  };
152
246
  type OverwriteOptions = Omit<CmsFieldBase, 'name'> & {
@@ -204,45 +298,11 @@ declare class VitePress {
204
298
  }): DecapCmsCollectionFile;
205
299
  static createDefaultPageFileCollection(name: string, files: Parameters<typeof VitePress['createDefaultPageFile']>[], options?: {
206
300
  collection?: Partial<Omit<DecapCmsCollection<'file'>, 'name' | 'files'>>;
207
- }): KeysToCamelCase<Omit<decap_cms_core.CmsCollection, "files" | "fields">> & {
301
+ }): KeysToCamelCase<Omit<decap_cms_core.CmsCollection, "fields" | "files">> & {
208
302
  files: DecapCmsCollectionFile[];
209
303
  };
210
304
  }
211
305
 
212
- declare function VitePluginDecapCMS(options: Options): {
213
- name: string;
214
- configResolved(this: void, config: Readonly<Omit<vite.UserConfig, "plugins" | "css" | "assetsInclude" | "optimizeDeps" | "worker" | "build"> & {
215
- configFile: string | undefined;
216
- configFileDependencies: string[];
217
- inlineConfig: vite.InlineConfig;
218
- root: string;
219
- base: string;
220
- publicDir: string;
221
- cacheDir: string;
222
- command: "build" | "serve";
223
- mode: string;
224
- isWorker: boolean;
225
- isProduction: boolean;
226
- envDir: string;
227
- env: Record<string, any>;
228
- resolve: Required<vite.ResolveOptions> & {
229
- alias: vite.Alias[];
230
- };
231
- plugins: readonly Plugin<any>[];
232
- css: vite.ResolvedCSSOptions;
233
- esbuild: false | vite.ESBuildOptions;
234
- server: vite.ResolvedServerOptions;
235
- build: vite.ResolvedBuildOptions;
236
- preview: vite.ResolvedPreviewOptions;
237
- ssr: vite.ResolvedSSROptions;
238
- assetsInclude: (file: string) => boolean;
239
- logger: vite.Logger;
240
- createResolver: (options?: Partial<vite.InternalResolveOptions> | undefined) => vite.ResolveFn;
241
- optimizeDeps: vite.DepOptimizationOptions;
242
- worker: vite.ResolvedWorkerOptions;
243
- appType: vite.AppType;
244
- experimental: vite.ExperimentalOptions;
245
- } & vite.PluginHookUtils>): Promise<void>;
246
- };
306
+ declare function VitePluginDecapCMS(options: Options): Plugin;
247
307
 
248
- export { type CdnLinkOptions, type CollectionType, type DecapCmsCollection, type DecapCmsCollectionFile, type DecapCmsConfig, type DecapCmsField, type DecapCmsFieldType, type DecapCmsFieldWidget, type DecapCmsMarkdownFieldRenderOptions, type KeysToCamelCase, type KeysToSnakeCase, type LoginPageOptions, type Options, type OverwriteOptions, VitePress, type VitePressDefaultThemeFieldOptions, type VitePressDefaultThemeFrontmatterKeys, type VitePressFieldOptions, type VitePressPageFrontmatterKeys, createField, createFile, createFileCollection, createFolderCollection, VitePluginDecapCMS as default, getGitData };
308
+ export { type CdnLinkOptions, type CollectionType, type DecapCmsCollection, type DecapCmsCollectionFile, type DecapCmsConfig, type DecapCmsField, type DecapCmsFieldType, type DecapCmsFieldWidget, type DecapCmsMarkdownFieldRenderOptions, type DecapProxyOptions, type EnvContextOption, type EnvDevContextOption, type HeadConfig, type KeysToCamelCase, type KeysToSnakeCase, type LoginPageOptions, type Options, type OverwriteOptions, VitePress, type VitePressDefaultThemeFieldOptions, type VitePressDefaultThemeFrontmatterKeys, type VitePressFieldOptions, type VitePressPageFrontmatterKeys, createField, createFile, createFileCollection, createFolderCollection, VitePluginDecapCMS as default, getGitData };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,24 @@
1
- import * as vite from 'vite';
2
1
  import { Plugin } from 'vite';
3
2
  import * as yaml from 'yaml';
4
3
  import * as decap_cms_core from 'decap-cms-core';
5
- import { CmsEventListener, CMS, CmsFieldMarkdown, CmsField, CmsFieldMeta, CmsCollectionFile, CmsConfig, CmsLocalBackend, CmsBackend, CmsFieldStringOrText, CmsFieldBase, CmsCollection } from 'decap-cms-core';
4
+ import { CmsEventListener, CMS, EditorComponentOptions, Formatter, CmsFieldMarkdown, CmsField, CmsFieldMeta, CmsCollectionFile, CmsConfig, CmsLocalBackend, CmsBackend, CmsFieldStringOrText, CmsFieldBase, CmsCollection } from 'decap-cms-core';
5
+ import { ExecOptions } from 'child_process';
6
6
 
7
7
  interface CmsHookContext {
8
8
  app: CMS;
9
9
  }
10
10
  type CmsEventHookContext = CmsHookContext & Parameters<CmsEventListener['handler']>[0];
11
+ type CmsEditorComponentOptions = EditorComponentOptions & {
12
+ id: string;
13
+ label: string;
14
+ pattern: RegExp;
15
+ fields: DecapCmsField[];
16
+ };
17
+ interface CmsEditorFormatter {
18
+ name: string;
19
+ extension: string;
20
+ formatter: Formatter;
21
+ }
11
22
  type ScriptOptions = {
12
23
  [event in `on${Capitalize<CmsEventListener['name']>}`]?: (ctx: CmsEventHookContext) => Promise<void> | void;
13
24
  } & {
@@ -40,8 +51,34 @@ type ScriptOptions = {
40
51
  *
41
52
  * cms.init()
42
53
  * ```
54
+ *
55
+ * @see https://decapcms.org/docs/manual-initialization/
56
+ * @default false
43
57
  */
44
58
  useManualInitialization?: boolean;
59
+ /**
60
+ * Register custom components to use in the rich text markdown editor field
61
+ * @see https://decapcms.org/docs/custom-widgets/
62
+ */
63
+ markdownEditorComponents?: CmsEditorComponentOptions[];
64
+ /**
65
+ * Register custom file formatters.
66
+ * @see https://decapcms.org/docs/custom-formatters/
67
+ * @default []
68
+ */
69
+ formatters?: CmsEditorFormatter[];
70
+ /**
71
+ * Register custom styles to use in the CMS
72
+ * Either pass the filename of the stylesheet or with `options.raw` pass the raw styles imported.
73
+ * @see https://decapcms.org/docs/customization/
74
+ * @default []
75
+ */
76
+ previewStylesheets?: (string | {
77
+ style: string;
78
+ options: {
79
+ raw: true;
80
+ };
81
+ })[];
45
82
  };
46
83
 
47
84
  type CamelToSnakeCase<S extends string, I extends string = never> = S extends `${infer T}${infer U}` ? S extends I ? S : `${T extends Capitalize<T> ? '_' : ''}${Lowercase<T>}${CamelToSnakeCase<U>}` : S;
@@ -53,6 +90,8 @@ type KeysToCamelCase<T> = {
53
90
  [K in keyof T as CamelCase<string & K>]: T[K] extends {} ? KeysToCamelCase<T[K]> : T[K];
54
91
  };
55
92
  type PickRequired<O extends object, K extends keyof O> = Omit<O, K> & Required<Pick<O, K>>;
93
+ type EnvContextOption = boolean | 'dev' | 'prod';
94
+ type EnvDevContextOption = Exclude<EnvContextOption, 'prod'>;
56
95
  type CollectionType = 'file' | 'folder';
57
96
  type DecapCmsMarkdownFieldRenderOptions = KeysToCamelCase<Omit<CmsFieldMarkdown, 'widget' | 'default' | 'editorComponents'>>;
58
97
  type DecapCmsField = KeysToCamelCase<CmsField>;
@@ -70,7 +109,7 @@ type DecapCmsCollection<Type extends CollectionType = CollectionType> = Type ext
70
109
  }> : never;
71
110
  type DecapCmsConfig = KeysToCamelCase<Omit<CmsConfig, 'local_backend' | 'backend' | 'collections' | 'load_config_file'>> & {
72
111
  backend: {
73
- local?: boolean | 'dev' | KeysToCamelCase<CmsLocalBackend>;
112
+ local?: EnvDevContextOption | KeysToCamelCase<CmsLocalBackend>;
74
113
  /**
75
114
  * Overwrite the branch specified in `backend.branch`
76
115
  * - true: always use current branch
@@ -79,7 +118,7 @@ type DecapCmsConfig = KeysToCamelCase<Omit<CmsConfig, 'local_backend' | 'backend
79
118
  * - 'prod': ony use the current branch when building the site
80
119
  * @default false
81
120
  */
82
- useCurrentBranch?: boolean | 'dev' | 'prod';
121
+ useCurrentBranch?: EnvContextOption;
83
122
  } & KeysToCamelCase<CmsBackend>;
84
123
  collections: DecapCmsCollection[];
85
124
  /**
@@ -93,11 +132,62 @@ type CdnLinkOptions = string | {
93
132
  version?: string;
94
133
  base?: string;
95
134
  };
135
+ type HeadConfig = string | [string, Record<string, string>] | [string, Record<string, string>, string];
96
136
  interface LoginPageOptions {
137
+ /**
138
+ * The title for the CMS pages
139
+ * @default 'Content Manager'
140
+ */
97
141
  title?: string;
98
- head?: string[];
142
+ /**
143
+ * The favicon for the CMS pages
144
+ */
145
+ icon?: string;
146
+ /**
147
+ * Additional head items for the page.
148
+ * The following items are configured already:
149
+ * - title
150
+ * - viewport
151
+ * - robots
152
+ * - charset
153
+ * - favicon (if used in the config)
154
+ * - Netlify Identity script (if used in the config)
155
+ * - custom config path (if used in the config)
156
+ */
157
+ head?: HeadConfig[];
158
+ /**
159
+ * Replace the login page with your own html
160
+ */
161
+ html?: string;
162
+ /**
163
+ * Instead of replacing all html, load this next to the CMS Editor for a custom footer, nav, etc.
164
+ */
165
+ additionalHtml?: string;
166
+ /**
167
+ * The version of Netlify Identity to use
168
+ * @default '1'
169
+ */
170
+ netlifyIdentityVersion?: string;
99
171
  }
100
172
  type YmlStringifyOptions = Parameters<typeof yaml.stringify>;
173
+ interface DecapProxyOptions {
174
+ /**
175
+ * If using local backend AND Vite dev mode is running, control whether to run the decap-server proxy.
176
+ * @default true
177
+ */
178
+ enabled?: boolean;
179
+ /**
180
+ * Run the proxy on a different port.
181
+ * Does not change the local backend allowed hosts
182
+ * @default 8081
183
+ */
184
+ port?: number;
185
+ /**
186
+ * Pass any option to use in the child process
187
+ * @default undefined
188
+ */
189
+ process?: ExecOptions;
190
+ }
101
191
  interface Options {
102
192
  /**
103
193
  * How to load Decap CMS
@@ -128,6 +218,10 @@ interface Options {
128
218
  * Run custom JS to enhance the CMS
129
219
  */
130
220
  script?: ScriptOptions;
221
+ /**
222
+ * Options for the Decap server proxy
223
+ */
224
+ proxy?: DecapProxyOptions;
131
225
  /**
132
226
  * Yml stringify options for writing the config.yml file
133
227
  */
@@ -142,11 +236,11 @@ declare function getGitData(): {
142
236
  readonly commitSha: string | undefined;
143
237
  };
144
238
  declare function createField<T extends DecapCmsFieldType>(widget: T, data: Omit<DecapCmsFieldWidget<T>, 'widget'>): DecapCmsFieldWidget<T>;
145
- declare function createFolderCollection(data: DecapCmsCollection<'folder'>): KeysToCamelCase<Omit<decap_cms_core.CmsCollection, "files" | "fields">> & {
239
+ declare function createFolderCollection(data: DecapCmsCollection<'folder'>): KeysToCamelCase<Omit<decap_cms_core.CmsCollection, "fields" | "files">> & {
146
240
  fields: DecapCmsField[];
147
241
  };
148
242
  declare function createFile(data: DecapCmsCollectionFile): DecapCmsCollectionFile;
149
- declare function createFileCollection(data: DecapCmsCollection<'file'>): KeysToCamelCase<Omit<decap_cms_core.CmsCollection, "files" | "fields">> & {
243
+ declare function createFileCollection(data: DecapCmsCollection<'file'>): KeysToCamelCase<Omit<decap_cms_core.CmsCollection, "fields" | "files">> & {
150
244
  files: DecapCmsCollectionFile[];
151
245
  };
152
246
  type OverwriteOptions = Omit<CmsFieldBase, 'name'> & {
@@ -204,45 +298,11 @@ declare class VitePress {
204
298
  }): DecapCmsCollectionFile;
205
299
  static createDefaultPageFileCollection(name: string, files: Parameters<typeof VitePress['createDefaultPageFile']>[], options?: {
206
300
  collection?: Partial<Omit<DecapCmsCollection<'file'>, 'name' | 'files'>>;
207
- }): KeysToCamelCase<Omit<decap_cms_core.CmsCollection, "files" | "fields">> & {
301
+ }): KeysToCamelCase<Omit<decap_cms_core.CmsCollection, "fields" | "files">> & {
208
302
  files: DecapCmsCollectionFile[];
209
303
  };
210
304
  }
211
305
 
212
- declare function VitePluginDecapCMS(options: Options): {
213
- name: string;
214
- configResolved(this: void, config: Readonly<Omit<vite.UserConfig, "plugins" | "css" | "assetsInclude" | "optimizeDeps" | "worker" | "build"> & {
215
- configFile: string | undefined;
216
- configFileDependencies: string[];
217
- inlineConfig: vite.InlineConfig;
218
- root: string;
219
- base: string;
220
- publicDir: string;
221
- cacheDir: string;
222
- command: "build" | "serve";
223
- mode: string;
224
- isWorker: boolean;
225
- isProduction: boolean;
226
- envDir: string;
227
- env: Record<string, any>;
228
- resolve: Required<vite.ResolveOptions> & {
229
- alias: vite.Alias[];
230
- };
231
- plugins: readonly Plugin<any>[];
232
- css: vite.ResolvedCSSOptions;
233
- esbuild: false | vite.ESBuildOptions;
234
- server: vite.ResolvedServerOptions;
235
- build: vite.ResolvedBuildOptions;
236
- preview: vite.ResolvedPreviewOptions;
237
- ssr: vite.ResolvedSSROptions;
238
- assetsInclude: (file: string) => boolean;
239
- logger: vite.Logger;
240
- createResolver: (options?: Partial<vite.InternalResolveOptions> | undefined) => vite.ResolveFn;
241
- optimizeDeps: vite.DepOptimizationOptions;
242
- worker: vite.ResolvedWorkerOptions;
243
- appType: vite.AppType;
244
- experimental: vite.ExperimentalOptions;
245
- } & vite.PluginHookUtils>): Promise<void>;
246
- };
306
+ declare function VitePluginDecapCMS(options: Options): Plugin;
247
307
 
248
- export { type CdnLinkOptions, type CollectionType, type DecapCmsCollection, type DecapCmsCollectionFile, type DecapCmsConfig, type DecapCmsField, type DecapCmsFieldType, type DecapCmsFieldWidget, type DecapCmsMarkdownFieldRenderOptions, type KeysToCamelCase, type KeysToSnakeCase, type LoginPageOptions, type Options, type OverwriteOptions, VitePress, type VitePressDefaultThemeFieldOptions, type VitePressDefaultThemeFrontmatterKeys, type VitePressFieldOptions, type VitePressPageFrontmatterKeys, createField, createFile, createFileCollection, createFolderCollection, VitePluginDecapCMS as default, getGitData };
308
+ export { type CdnLinkOptions, type CollectionType, type DecapCmsCollection, type DecapCmsCollectionFile, type DecapCmsConfig, type DecapCmsField, type DecapCmsFieldType, type DecapCmsFieldWidget, type DecapCmsMarkdownFieldRenderOptions, type DecapProxyOptions, type EnvContextOption, type EnvDevContextOption, type HeadConfig, type KeysToCamelCase, type KeysToSnakeCase, type LoginPageOptions, type Options, type OverwriteOptions, VitePress, type VitePressDefaultThemeFieldOptions, type VitePressDefaultThemeFrontmatterKeys, type VitePressFieldOptions, type VitePressPageFrontmatterKeys, createField, createFile, createFileCollection, createFolderCollection, VitePluginDecapCMS as default, getGitData };
package/dist/index.js CHANGED
@@ -31,6 +31,7 @@ var __objRest = (source, exclude) => {
31
31
  };
32
32
 
33
33
  // src/index.ts
34
+ import { exec } from "child_process";
34
35
  import { stringify } from "yaml";
35
36
 
36
37
  // src/util.ts
@@ -112,35 +113,41 @@ var VitePress = class {
112
113
  createOverwriteableField("boolean", {
113
114
  name: "sidebar",
114
115
  label: "Whether to display the sidebar",
115
- default: true
116
+ default: true,
117
+ required: false
116
118
  }, overwrites == null ? void 0 : overwrites.sidebar),
117
119
  // TODO: add aside 'left' option
118
120
  createOverwriteableField("boolean", {
119
121
  name: "aside",
120
122
  label: "Whether to display the aside container",
121
- default: true
123
+ default: true,
124
+ required: false
122
125
  }, overwrites == null ? void 0 : overwrites.aside),
123
126
  // TODO: add support for [number, number] | 'deep' | false
124
127
  createOverwriteableField("number", {
125
128
  name: "outline",
126
129
  label: "The header levels in the outline",
127
- default: 2
130
+ default: 2,
131
+ required: false
128
132
  }, overwrites == null ? void 0 : overwrites.outline),
129
133
  // TODO: add support for Date
130
134
  createOverwriteableField("boolean", {
131
135
  name: "lastUpdated",
132
136
  label: "Whether to display last updated text",
133
- default: true
137
+ default: true,
138
+ required: false
134
139
  }, overwrites == null ? void 0 : overwrites.lastUpdated),
135
140
  createOverwriteableField("boolean", {
136
141
  name: "editLink",
137
142
  label: "Whether to display edit link text",
138
- default: true
143
+ default: true,
144
+ required: false
139
145
  }, overwrites == null ? void 0 : overwrites.editLink),
140
146
  createOverwriteableField("boolean", {
141
147
  name: "footer",
142
148
  label: "Whether to display footer text",
143
- default: true
149
+ default: true,
150
+ required: false
144
151
  }, overwrites == null ? void 0 : overwrites.footer),
145
152
  createOverwriteableField("string", {
146
153
  name: "pageClass",
@@ -269,71 +276,148 @@ function createConfigFile(config, command) {
269
276
  }
270
277
 
271
278
  // src/script.ts
279
+ function createCmsFunction(method, items, createParams, options) {
280
+ var _a;
281
+ const create = (params) => {
282
+ var _a2;
283
+ return `${(_a2 = options == null ? void 0 : options.base) != null ? _a2 : "CMS"}.${method}(${params})`;
284
+ };
285
+ return (items != null ? items : []).map((item) => {
286
+ const params = createParams(item);
287
+ if (!params)
288
+ return null;
289
+ else
290
+ return create(params);
291
+ }).filter(Boolean).join((_a = options == null ? void 0 : options.joinChar) != null ? _a : "\n");
292
+ }
272
293
  function createScript(options) {
273
294
  const _a = options, {
274
295
  useManualInitialization,
296
+ markdownEditorComponents,
297
+ formatters,
298
+ previewStylesheets,
275
299
  onGenerated: onGenerated,
276
300
  onInitialized
277
301
  } = _a, eventHooks = __objRest(_a, [
278
302
  "useManualInitialization",
303
+ "markdownEditorComponents",
304
+ "formatters",
305
+ "previewStylesheets",
306
+ // previewTemplates,
307
+ // widgets,
279
308
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
280
309
  "onGenerated",
281
310
  "onInitialized"
282
311
  ]);
283
- const events = Object.keys(eventHooks).map((hookName) => {
312
+ const events = createCmsFunction("registerEventListener", Object.keys(eventHooks), (hookName) => {
284
313
  const hook = eventHooks[hookName];
285
314
  if (!hook)
286
315
  return null;
287
316
  else {
288
317
  const name = hookName.slice(2)[0].toLowerCase() + hookName.slice(3);
289
- return `CMS.registerEventListener({ name: '${name}', handler: data => { function ${hook.toString()}; ${hookName}({ app: CMS, ...data }) } })`;
318
+ return `{ name: '${name}', handler: data => { function ${hook.toString()}; ${hookName}({ app: CMS, ...data }) } }`;
290
319
  }
291
- }).join("\n");
320
+ });
321
+ const customFormatters = createCmsFunction("registerCustomFormat", formatters, ({ name, extension, formatter }) => {
322
+ return `'${name}', '${extension}', ${formatter.toString()}`;
323
+ });
324
+ const customStyles = createCmsFunction("registerPreviewStyle", previewStylesheets, (style) => {
325
+ return typeof style === "string" ? style : `${style.style}, { raw: ${style.options.raw} }`;
326
+ });
327
+ const editorComponents = createCmsFunction("registerEditorComponent", markdownEditorComponents, (item) => {
328
+ const _a2 = item, { pattern, toPreview, toBlock, fromBlock } = _a2, component = __objRest(_a2, ["pattern", "toPreview", "toBlock", "fromBlock"]);
329
+ return `{ pattern: ${pattern}, toPreview: ${toPreview.toString()}, toBlock: ${toBlock.toString()}, fromBlock: ${fromBlock.toString()}, ...${JSON.stringify(component)}}`;
330
+ });
292
331
  return `
293
332
  <script>
294
333
  ${useManualInitialization ? "window.CMS_MANUAL_INIT = true;" : ""}
295
334
  ${onInitialized != void 0 ? `window.onload = () => { function ${onInitialized.toString()}; onInitialized({ app: CMS }) }` : ""}
335
+ ${customFormatters}
336
+ ${customStyles}
296
337
  ${events}
338
+ ${editorComponents}
297
339
  </script>`;
298
340
  }
299
341
 
300
342
  // src/files/index.ts
343
+ var defaultDecapCmsCdnVersion = "3.1.3";
344
+ var defaultNetlifyIdentityVersion = "1";
345
+ var addSlash = (path, slash = "/") => path.endsWith(slash) ? path : path + slash;
301
346
  function resolveCdnRoute(options) {
302
- const getUrl = (host = "https://unpkg.com/", version = "3.1.3") => {
303
- return `${host.endsWith("/") ? host : host + "/"}decap-cms@^${version}/dist/decap-cms.js`;
347
+ const getUrl = (host = "https://unpkg.com/", version = defaultDecapCmsCdnVersion) => {
348
+ return `${addSlash(host)}decap-cms@^${version}/dist/decap-cms.js`;
304
349
  };
305
350
  return typeof options === "boolean" ? options ? getUrl() : void 0 : typeof options === "string" ? options : options != void 0 ? getUrl(options.base, options.version) : void 0;
306
351
  }
307
- function getIndexFeatures(options) {
308
- var _a, _b;
309
- function useNetlifyIdentity(options2) {
310
- return options2.config.backend.name === "git-gateway";
311
- }
352
+ function resolveHead(head) {
353
+ return head.reduce((output, config) => {
354
+ if (typeof config === "string")
355
+ return output.concat(config);
356
+ if ("skip" in config) {
357
+ if (config.skip)
358
+ return output;
359
+ if (typeof config.head === "string")
360
+ return output.concat(config.head);
361
+ }
362
+ const item = "head" in config ? config.head : config;
363
+ let str = `<${item[0]}`;
364
+ for (const key in item[1]) {
365
+ str += ` ${key}="${item[1][key]}"`;
366
+ }
367
+ str += item[0] === "meta" ? "/>" : ">";
368
+ if (item[2] == void 0)
369
+ return output.concat(str);
370
+ str += item[2] + `</${item[0]}>`;
371
+ return output.concat(str);
372
+ }, []);
373
+ }
374
+ function getIndexFeatures(config, loadOptions) {
375
+ var _a;
376
+ const configRoute = config.dir ? addSlash(config.dir) + "config.yml" : void 0;
312
377
  return {
313
- cdn_route: resolveCdnRoute(options.load == void 0 || options.load.method === "cdn" ? (_b = (_a = options.load) == null ? void 0 : _a.options) != null ? _b : true : void 0),
314
- custom_logo: "logoUrl" in options.config ? options.config.logoUrl != void 0 : "logo_url" in options.config ? options.config.logo_url != void 0 : false,
315
- netlify_identity: useNetlifyIdentity(options),
316
- config_route: options.config.dir ? options.config.dir + (!options.config.dir.endsWith("/") ? "/" : "") + "config.yml" : void 0
378
+ cdn_route: resolveCdnRoute(loadOptions == void 0 || loadOptions.method === "cdn" ? (_a = loadOptions == null ? void 0 : loadOptions.options) != null ? _a : true : void 0),
379
+ custom_logo: "logoUrl" in config ? config.logoUrl != void 0 : "logo_url" in config ? config.logo_url != void 0 : false,
380
+ head: (options) => {
381
+ var _a2, _b, _c;
382
+ return resolveHead([
383
+ ["meta", { charset: "utf-8" }],
384
+ ["meta", { name: "viewport", content: "width=device-width, initial-scale=1.0" }],
385
+ ["meta", { name: "robots", content: "noindex" }],
386
+ ...(_a2 = options == null ? void 0 : options.head) != null ? _a2 : [],
387
+ ["title", {}, (_b = options == null ? void 0 : options.title) != null ? _b : "Content Manager"],
388
+ {
389
+ head: ["link", { rel: "favicon", ref: (_c = options == null ? void 0 : options.icon) != null ? _c : "" }],
390
+ skip: (options == null ? void 0 : options.icon) == void 0
391
+ },
392
+ {
393
+ head: ["script", { src: `https://identity.netlify.com/v${defaultNetlifyIdentityVersion}/netlify-identity-widget.js` }],
394
+ skip: config.backend.name !== "git-gateway"
395
+ },
396
+ {
397
+ head: ["link", { type: "text/yaml", rel: "cms-config-url", href: configRoute }],
398
+ skip: configRoute == void 0
399
+ }
400
+ ]);
401
+ }
317
402
  };
318
403
  }
319
- function createIndexFile(_options) {
404
+ function createIndexFile(pluginOptions) {
320
405
  var _a, _b;
321
- const features = getIndexFeatures(_options);
322
- const options = _options.login;
323
- const identifyScript = '<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>';
406
+ const { config, load, login: options, script } = pluginOptions;
407
+ if (options == null ? void 0 : options.html)
408
+ return options.html;
409
+ const features = getIndexFeatures(config, load);
324
410
  return `<!DOCTYPE html>
325
411
  <html>
326
412
  <head>
327
- <meta charset="utf-8" />
328
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
329
- <meta name="robots" content="noindex" />
330
- <title>${(_a = options == null ? void 0 : options.title) != null ? _a : "Content Manager"}</title>${features.netlify_identity ? identifyScript : ""}
331
- ${features.config_route ? `<link href="${features.config_route}" type="text/yaml" rel="cms-config-url">` : ""}
332
- ${((_b = options == null ? void 0 : options.head) != null ? _b : []).join("\n" + " ".repeat(8))}
413
+ ${features.head(options).join("\n" + " ".repeat(8))}
333
414
  </head>
334
415
  <body>
335
416
  ${features.cdn_route ? `<script src="${features.cdn_route}"></script>` : ""}
336
- ${_options.script ? createScript(_options.script) : ""}
417
+ ${script ? createScript(script) : ""}
418
+ ${((_a = pluginOptions.login) == null ? void 0 : _a.additionalHtml) ? `${(_b = pluginOptions.login) == null ? void 0 : _b.additionalHtml}
419
+
420
+ <div id="nc-root"></div>` : ""}
337
421
  </body>
338
422
  </html>${features.custom_logo ? `
339
423
 
@@ -367,8 +451,24 @@ function validateLoadOptions(options) {
367
451
  if (!valid)
368
452
  throw new Error("Invalid load options for decap-cms provided");
369
453
  }
454
+ function runProxy(options) {
455
+ var _a, _b, _c, _d, _e;
456
+ const proxy = exec("npx decap-server", __spreadProps(__spreadValues({}, (_a = options == null ? void 0 : options.process) != null ? _a : {}), {
457
+ env: __spreadProps(__spreadValues({}, (_c = (_b = options == null ? void 0 : options.process) == null ? void 0 : _b.env) != null ? _c : {}), {
458
+ PORT: ((_d = options == null ? void 0 : options.port) != null ? _d : 8081).toString()
459
+ })
460
+ }));
461
+ (_e = proxy.stdout) == null ? void 0 : _e.pipe(process.stdout);
462
+ proxy.on("error", (err) => {
463
+ if ("code" in err && err.code === "EADDRINUSE") {
464
+ console.log("[PROXY] Port is already used");
465
+ } else
466
+ throw err;
467
+ });
468
+ process.on("beforeExit", () => proxy.kill());
469
+ }
370
470
  async function updateConfig(options, config) {
371
- var _a, _b, _c, _d, _e, _f;
471
+ var _a, _b, _c, _d, _e, _f, _g, _h;
372
472
  validateLoadOptions(options.load);
373
473
  const loginFile = createIndexFile(options);
374
474
  const configFile = createConfigFile(options.config, config.command);
@@ -383,9 +483,12 @@ async function updateConfig(options, config) {
383
483
  ]
384
484
  }
385
485
  );
386
- await ((_d = (_c = options.script) == null ? void 0 : _c.onConfigUpdated) == null ? void 0 : _d.call(_c));
486
+ if (config.command === "serve" && configFile.local_backend !== false && ((_d = (_c = options.proxy) == null ? void 0 : _c.enabled) != null ? _d : true)) {
487
+ runProxy(options.proxy);
488
+ }
489
+ await ((_f = (_e = options.script) == null ? void 0 : _e.onConfigUpdated) == null ? void 0 : _f.call(_e));
387
490
  if (config.command === "build") {
388
- await ((_f = (_e = options.script) == null ? void 0 : _e.onGenerated) == null ? void 0 : _f.call(_e));
491
+ await ((_h = (_g = options.script) == null ? void 0 : _g.onGenerated) == null ? void 0 : _h.call(_g));
389
492
  }
390
493
  }
391
494
  function VitePluginDecapCMS(options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-decap-cms",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Simplify the configuration of Decap cms for Vite projects",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -32,21 +32,23 @@
32
32
  "build": "tsup src/index.ts --dts --format cjs,esm"
33
33
  },
34
34
  "dependencies": {
35
+ "decap-server": "^3.0.4",
36
+ "veaury": "^2.3.18",
35
37
  "yaml": "^2.4.0"
36
38
  },
37
39
  "devDependencies": {
38
40
  "@types/node": "18",
39
- "@typescript-eslint/eslint-plugin": "^7.1.1",
40
- "@typescript-eslint/parser": "^7.1.1",
41
- "decap-cms-core": "^3.3.3",
42
- "eslint": "^8.54.0",
41
+ "@typescript-eslint/eslint-plugin": "^7.4.0",
42
+ "@typescript-eslint/parser": "^7.4.0",
43
+ "decap-cms-core": "^3.3.5",
44
+ "eslint": "^8.57.0",
43
45
  "tsup": "^8.0.2",
44
- "typescript": "^5.3.2",
45
- "vite": "^5.0.2",
46
- "vitest": "^0.34.6",
47
- "vue": "^3.3.9"
46
+ "typescript": "^5.4.3",
47
+ "vite": "^5.2.6",
48
+ "vitest": "^1.4.0",
49
+ "vue": "^3.4.21"
48
50
  },
49
51
  "optionalDependencies": {
50
- "decap-cms-app": "^3.1.2"
52
+ "decap-cms-app": "^3.1.5"
51
53
  }
52
54
  }