@caiquecamargo/vite-plugin-netlify-cms 0.0.17 → 0.1.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
@@ -1,2 +1,52 @@
1
1
  # vite-plugin-netlify-cms
2
- Simple plugin to generate config.yml to NetlifyCMS
2
+
3
+ Simple plugin to generate config.yml for Netlify CMS / Sveltia CMS with TypeScript support.
4
+
5
+ ## Features
6
+
7
+ - 🎯 TypeScript-first configuration
8
+ - 📦 Automatic `config.yml` generation
9
+ - 🔐 OAuth authentication (GitHub)
10
+ - ✨ **Astro Integration** for seamless SSR routing
11
+ - 🔌 **Vite Plugin** for other frameworks
12
+ - 🚀 Hot reload support
13
+ - ✨ Support for Sveltia CMS and Decap CMS
14
+ - 🎨 Type-safe widget definitions
15
+
16
+ ## OAuth Authentication
17
+
18
+ ### For Astro Projects (Recommended)
19
+
20
+ ```typescript
21
+ // astro.config.ts
22
+ import { defineConfig } from 'astro/config';
23
+ import { astroOAuthIntegration } from 'vite-plugin-netlify-cms';
24
+ import sveltiaCMS from 'vite-plugin-netlify-cms';
25
+
26
+ export default defineConfig({
27
+ integrations: [
28
+ astroOAuthIntegration(), // Adds /oauth and /oauth/callback routes
29
+ ],
30
+ vite: {
31
+ plugins: [sveltiaCMS({ type: 'sveltia' })],
32
+ },
33
+ output: 'server', // or 'hybrid'
34
+ });
35
+ ```
36
+
37
+ ### For Other Projects (Vite, SvelteKit, Nuxt, etc.)
38
+
39
+ ```typescript
40
+ // vite.config.ts
41
+ import { defineConfig } from 'vite';
42
+ import sveltiaCMS, { oauthPlugin } from 'vite-plugin-netlify-cms';
43
+
44
+ export default defineConfig({
45
+ plugins: [
46
+ sveltiaCMS({ type: 'sveltia' }),
47
+ oauthPlugin(), // Works in dev, needs serverless setup for production
48
+ ],
49
+ });
50
+ ```
51
+
52
+ **[📖 Complete OAuth Setup Guide](./OAUTH_SETUP.md)**
package/dist/index.d.ts CHANGED
@@ -1,23 +1,57 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
- type Widgets = 'boolean' | 'code' | 'color' | 'datetime' | 'hidden' | 'file' | 'image' | 'list' | 'map' | 'number' | 'object' | 'relation' | 'select' | 'string' | 'text' | 'markdown';
3
+ interface OAuthPluginOptions {
4
+ /**
5
+ * OAuth login route
6
+ * @default '/oauth'
7
+ */
8
+ loginRoute?: string;
9
+ /**
10
+ * OAuth callback route
11
+ * @default '/oauth/callback'
12
+ */
13
+ callbackRoute?: string;
14
+ /**
15
+ * Disable OAuth plugin
16
+ * @default false
17
+ */
18
+ disabled?: boolean;
19
+ }
20
+ /**
21
+ * Vite plugin for OAuth authentication routes
22
+ * Adds /oauth and /oauth/callback routes for GitHub authentication
23
+ */
24
+ declare function oauthPlugin(options?: OAuthPluginOptions): Plugin;
25
+
26
+ type Widgets = 'boolean' | 'code' | 'color' | 'compute' | 'datetime' | 'hidden' | 'file' | 'image' | 'keyvalue' | 'list' | 'map' | 'markdown' | 'number' | 'object' | 'relation' | 'richtext' | 'select' | 'string' | 'text' | 'uuid';
27
+ type LocaleCode = string;
4
28
  interface Widget {
5
29
  name: string;
6
30
  label?: string;
7
31
  widget: Widgets;
8
- required?: boolean;
9
- pattern?: string[];
32
+ required?: boolean | LocaleCode[];
33
+ pattern?: [string, string];
10
34
  comment?: string;
35
+ hint?: string;
36
+ preview?: boolean;
37
+ i18n?: boolean | 'duplicate' | 'translate' | 'none';
38
+ readonly?: boolean;
11
39
  }
12
40
  interface BooleanWidget extends Widget {
13
41
  widget: 'boolean';
14
42
  default?: boolean;
43
+ before_input?: string;
44
+ after_input?: string;
15
45
  }
16
46
  interface CodeWidget extends Widget {
17
47
  widget: 'code';
48
+ default?: string | Record<string, string>;
18
49
  default_language?: string;
19
50
  allow_language_selection?: boolean;
20
- keys?: string;
51
+ keys?: {
52
+ code: string;
53
+ lang: string;
54
+ };
21
55
  output_code_only?: boolean;
22
56
  }
23
57
  interface ColorWidget extends Widget {
@@ -26,6 +60,10 @@ interface ColorWidget extends Widget {
26
60
  allowInput?: boolean;
27
61
  enableAlpha?: boolean;
28
62
  }
63
+ interface ComputeWidget extends Widget {
64
+ widget: 'compute';
65
+ value: string;
66
+ }
29
67
  interface DateTimeWidget extends Widget {
30
68
  widget: 'datetime';
31
69
  default?: string;
@@ -40,36 +78,61 @@ interface HiddenWidget extends Widget {
40
78
  }
41
79
  interface FileWidget extends Widget {
42
80
  widget: 'file';
43
- default?: string;
81
+ default?: string | string[];
44
82
  media_library?: Record<string, unknown>;
45
83
  allow_multiple?: boolean;
84
+ multiple?: boolean;
85
+ min?: number;
86
+ max?: number;
46
87
  config?: Record<string, unknown>;
47
88
  media_folder?: string;
89
+ public_folder?: string;
48
90
  choose_url?: boolean;
91
+ accept?: string;
92
+ media_libraries?: Record<string, unknown>;
49
93
  }
50
94
  interface ImageWidget extends Widget {
51
95
  widget: 'image';
52
- default?: string;
96
+ default?: string | string[];
53
97
  media_library?: Record<string, unknown>;
54
98
  allow_multiple?: boolean;
99
+ multiple?: boolean;
100
+ min?: number;
101
+ max?: number;
55
102
  config?: Record<string, unknown>;
56
103
  media_folder?: string;
104
+ public_folder?: string;
57
105
  choose_url?: boolean;
106
+ accept?: string;
107
+ media_libraries?: Record<string, unknown>;
108
+ }
109
+ interface KeyValueWidget extends Widget {
110
+ widget: 'keyvalue';
111
+ default?: Record<string, string>;
112
+ key_label?: string;
113
+ value_label?: string;
114
+ min?: number;
115
+ max?: number;
58
116
  }
59
117
  interface ListWidget extends Widget {
60
118
  widget: 'list';
61
- default?: string[] | CollectionField[];
119
+ default?: string[] | CollectionField[] | Record<string, unknown>;
62
120
  allow_add?: boolean;
63
- collapsed?: boolean;
121
+ allow_remove?: boolean;
122
+ allow_reorder?: boolean;
123
+ collapsed?: boolean | 'auto';
124
+ minimize_collapsed?: boolean | 'auto';
64
125
  summary?: string;
65
- minimize_collapsed?: boolean;
66
126
  label_singular?: string;
67
127
  field?: CollectionField;
68
128
  fields?: CollectionField[];
69
129
  types?: ObjectWidget[];
130
+ typeKey?: string;
70
131
  max?: number;
71
132
  min?: number;
72
133
  add_to_top?: boolean;
134
+ root?: boolean;
135
+ thumbnail?: string;
73
136
  }
74
137
  interface MapWidget extends Widget {
75
138
  widget: 'map';
@@ -80,63 +143,108 @@ interface MapWidget extends Widget {
80
143
  interface NumberWidget extends Widget {
81
144
  widget: 'number';
82
145
  default?: string | number;
83
- value_type?: 'int' | 'float';
146
+ value_type?: 'int' | 'float' | 'int/string' | 'float/string';
84
147
  min?: number;
85
148
  max?: number;
86
149
  step?: number;
150
+ before_input?: string;
151
+ after_input?: string;
87
152
  }
88
153
  interface ObjectWidget extends Widget {
89
154
  widget: 'object';
90
- default?: CollectionField[];
91
- collapsed?: boolean;
155
+ default?: CollectionField[] | Record<string, unknown>;
156
+ collapsed?: boolean | 'auto';
92
157
  summary?: string;
93
- fields: CollectionField[];
158
+ fields?: CollectionField[];
159
+ types?: VariableFieldType[];
160
+ typeKey?: string;
161
+ }
162
+ interface VariableFieldType {
163
+ name: string;
164
+ label?: string;
165
+ widget?: 'object';
166
+ summary?: string;
167
+ fields?: CollectionField[];
94
168
  }
95
169
  interface RelationWidget extends Widget {
96
170
  widget: 'relation';
97
- default?: any;
171
+ default?: any | any[];
98
172
  collection: string;
99
- value_field: string;
100
- search_fields: string[];
173
+ value_field?: string;
174
+ search_fields?: string[];
101
175
  file?: string;
102
176
  display_fields?: string[];
103
177
  multiple?: boolean;
104
178
  min?: number;
105
179
  max?: number;
106
180
  options_length?: number;
181
+ dropdown_threshold?: number;
182
+ filters?: Array<{
183
+ field: string;
184
+ values: any[];
185
+ }>;
107
186
  }
108
187
  interface SelectWidget extends Widget {
109
188
  widget: 'select';
110
- default?: string | {
189
+ default?: string | number | null | {
111
190
  label: string;
112
- value: string;
113
- };
114
- options?: string[] | {
191
+ value: string | number | null;
192
+ } | Array<string | number | null>;
193
+ options: Array<string | number | null | {
115
194
  label: string;
116
- value: string;
117
- }[];
195
+ value: string | number | null;
196
+ }>;
118
197
  multiple?: boolean;
119
198
  min?: number;
120
199
  max?: number;
200
+ dropdown_threshold?: number;
121
201
  }
122
202
  interface StringWidget extends Widget {
123
203
  widget: 'string';
124
204
  default?: string;
205
+ type?: 'text' | 'url' | 'email';
206
+ minlength?: number;
207
+ maxlength?: number;
208
+ prefix?: string;
209
+ suffix?: string;
210
+ before_input?: string;
211
+ after_input?: string;
125
212
  }
126
213
  interface TextWidget extends Widget {
127
214
  widget: 'text';
128
215
  default?: string;
216
+ minlength?: number;
217
+ maxlength?: number;
129
218
  }
130
- type MarkdownButtons = 'bold' | 'italic' | 'code' | 'link' | 'heading-one' | 'heading-two' | 'heading-three' | 'heading-four' | 'heading-five' | 'heading-six' | 'quote' | 'bulleted-list' | 'numbered-list';
219
+ type MarkdownButtons = 'bold' | 'italic' | 'code' | 'link' | 'heading-one' | 'heading-two' | 'heading-three' | 'heading-four' | 'heading-five' | 'heading-six' | 'quote' | 'bulleted-list' | 'numbered-list' | 'strikethrough';
220
+ type EditorComponent = 'code-block' | 'image' | string;
221
+ type EditorMode = 'rich_text' | 'raw';
131
222
  interface MarkdownWidget extends Widget {
132
223
  widget: 'markdown';
133
224
  default?: string;
134
225
  buttons?: MarkdownButtons[];
135
- editor_components?: ('image' | 'code-block')[];
136
- modes?: ('raw' | 'rich_text')[];
226
+ editor_components?: EditorComponent[];
227
+ modes?: EditorMode[];
137
228
  sanitize_preview?: boolean;
229
+ minimal?: boolean;
230
+ linked_images?: boolean;
138
231
  }
139
- type CollectionField = BooleanWidget | CodeWidget | ColorWidget | DateTimeWidget | HiddenWidget | FileWidget | ImageWidget | ListWidget | MapWidget | NumberWidget | ObjectWidget | RelationWidget | SelectWidget | StringWidget | TextWidget | MarkdownWidget;
232
+ interface RichTextWidget extends Widget {
233
+ widget: 'richtext';
234
+ default?: string;
235
+ buttons?: MarkdownButtons[];
236
+ editor_components?: EditorComponent[];
237
+ modes?: EditorMode[];
238
+ sanitize_preview?: boolean;
239
+ minimal?: boolean;
240
+ linked_images?: boolean;
241
+ }
242
+ interface UuidWidget extends Widget {
243
+ widget: 'uuid';
244
+ default?: string;
245
+ prefix?: string;
246
+ }
247
+ type CollectionField = BooleanWidget | CodeWidget | ColorWidget | ComputeWidget | DateTimeWidget | HiddenWidget | FileWidget | ImageWidget | KeyValueWidget | ListWidget | MapWidget | NumberWidget | ObjectWidget | RelationWidget | SelectWidget | StringWidget | TextWidget | MarkdownWidget | RichTextWidget | UuidWidget;
140
248
  interface Collection {
141
249
  /**
142
250
  * unique identifier for the collection, used as the key
@@ -163,6 +271,10 @@ interface Collection {
163
271
  * optional text, displayed below the label when viewing a collection
164
272
  */
165
273
  description?: string;
274
+ /**
275
+ * Material Symbols icon name
276
+ */
277
+ icon?: string;
166
278
  /**
167
279
  * for publish_mode: editorial_workflow only;
168
280
  * false hides UI publishing controls for a collection;
@@ -186,17 +298,21 @@ interface Collection {
186
298
  /**
187
299
  * These settings determine how collection files are parsed and saved.
188
300
  */
189
- extension?: 'yml' | 'yaml' | 'toml' | 'json' | 'md' | 'markdown' | 'html';
301
+ extension?: 'yml' | 'yaml' | 'toml' | 'json' | 'md' | 'markdown' | 'html' | string;
190
302
  /**
191
303
  * These settings determine how collection files are parsed and saved.
192
304
  */
193
- format?: 'yml' | 'yaml' | 'toml' | 'json' | 'frontmatter' | 'yaml-frontmatter' | 'toml-frontmatter' | 'json-frontmatter';
194
- frontmatter_delimiter?: string;
305
+ format?: 'yml' | 'yaml' | 'toml' | 'json' | 'frontmatter' | 'yaml-frontmatter' | 'toml-frontmatter' | 'json-frontmatter' | 'raw' | string;
306
+ frontmatter_delimiter?: string | string[];
195
307
  /**
196
308
  * specifies a template for generating new filenames
197
309
  * based on a file's creation date and title field
198
310
  */
199
311
  slug?: string;
312
+ /**
313
+ * The maximum number of characters allowed for an entry slug
314
+ */
315
+ slug_length?: number;
200
316
  /**
201
317
  * A string representing the path where content in this collection can be found on the live site.
202
318
  */
@@ -219,20 +335,97 @@ interface Collection {
219
335
  /**
220
336
  * An optional list of sort fields to show in the UI.
221
337
  */
222
- sortable_fields?: string;
338
+ sortable_fields?: string[] | {
339
+ fields: string[];
340
+ default?: {
341
+ field: string;
342
+ direction?: 'ascending' | 'descending';
343
+ };
344
+ };
223
345
  /**
224
346
  * An optional list of predefined view filters to show in the UI.
225
347
  */
226
- view_filters?: string;
348
+ view_filters?: Array<{
349
+ name?: string;
350
+ label: string;
351
+ field: string;
352
+ pattern: string | boolean;
353
+ }> | {
354
+ filters: Array<{
355
+ name?: string;
356
+ label: string;
357
+ field: string;
358
+ pattern: string | boolean;
359
+ }>;
360
+ default?: string;
361
+ };
227
362
  /**
228
363
  * An optional list of predefined view groups to show in the UI.
229
364
  */
230
- view_groups?: string;
365
+ view_groups?: Array<{
366
+ name?: string;
367
+ label: string;
368
+ field: string;
369
+ pattern?: string | boolean;
370
+ }> | {
371
+ groups: Array<{
372
+ name?: string;
373
+ label: string;
374
+ field: string;
375
+ pattern?: string | boolean;
376
+ }>;
377
+ default?: string;
378
+ };
379
+ /**
380
+ * Internal media folder path
381
+ */
382
+ media_folder?: string;
383
+ /**
384
+ * Public media folder path
385
+ */
386
+ public_folder?: string;
387
+ /**
388
+ * i18n options
389
+ */
390
+ i18n?: boolean | {
391
+ structure: 'single_file' | 'multiple_files' | 'multiple_folders' | 'multiple_folders_i18n_root';
392
+ locales: string[];
393
+ default_locale?: string;
394
+ };
395
+ /**
396
+ * YAML quote option
397
+ * @deprecated Use global yaml output options instead
398
+ */
399
+ yaml_quote?: boolean;
400
+ /**
401
+ * Entry thumbnail configuration
402
+ */
403
+ thumbnail?: boolean | string | string[];
404
+ /**
405
+ * Maximum number of entries that can be created
406
+ */
407
+ limit?: number;
231
408
  }
232
409
  interface FileCollection {
233
410
  name: string;
234
411
  label?: string;
412
+ label_singular?: string;
413
+ description?: string;
414
+ icon?: string;
235
415
  files: FileCollectionEntry[];
416
+ media_folder?: string;
417
+ public_folder?: string;
418
+ hide?: boolean;
419
+ publish?: boolean;
420
+ format?: string;
421
+ frontmatter_delimiter?: string | string[];
422
+ i18n?: boolean | Record<string, unknown>;
423
+ preview_path?: string;
424
+ preview_path_date_field?: string;
425
+ editor?: {
426
+ preview?: boolean;
427
+ };
428
+ yaml_quote?: boolean;
236
429
  }
237
430
  interface FileCollectionEntry extends Collection {
238
431
  /**
@@ -247,16 +440,54 @@ interface FolderCollection extends Collection {
247
440
  * details in Collection Types
248
441
  */
249
442
  folder: string;
443
+ /**
444
+ * Path template for entries
445
+ */
446
+ path?: string;
250
447
  /**
251
448
  * optional filter for folder collections; details in Collection Types
252
449
  */
253
- filter?: string;
450
+ filter?: {
451
+ field: string;
452
+ value?: any | any[];
453
+ pattern?: string;
454
+ };
254
455
  /**
255
456
  * true allows users to create new items in the collection;
256
457
  *
257
- * defaults to false
458
+ * defaults to false (in Decap), true (in Sveltia)
258
459
  */
259
460
  create?: boolean;
461
+ /**
462
+ * Nested collection options
463
+ */
464
+ nested?: {
465
+ depth?: number;
466
+ summary?: string;
467
+ subfolders?: boolean;
468
+ };
469
+ /**
470
+ * Meta data for nested collections
471
+ */
472
+ meta?: {
473
+ path?: {
474
+ widget?: 'string';
475
+ label?: string;
476
+ index_file?: string;
477
+ };
478
+ };
479
+ /**
480
+ * Index file inclusion options
481
+ */
482
+ index_file?: boolean | {
483
+ name?: string;
484
+ label?: string;
485
+ icon?: string;
486
+ fields?: CollectionField[];
487
+ editor?: {
488
+ preview?: boolean;
489
+ };
490
+ };
260
491
  }
261
492
  type Locales = 'bg' | 'ca' | 'cs' | 'da' | 'de' | 'en' | 'es' | 'fa' | 'fr' | 'he' | 'hr' | 'hu' | 'it' | 'ja' | 'ko' | 'lt' | 'nb_no' | 'nl' | 'nn_no' | 'pl' | 'pt' | 'ro' | 'ru' | 'sl' | 'sv' | 'th' | 'tr' | 'uk' | 'vi' | 'zh-Hans' | 'zh-Hant';
262
493
  interface CloudinaryMediaLibrary {
@@ -333,11 +564,15 @@ interface NetlifyCMSConfig {
333
564
  * @see https://decapcms.org/docs/beta-features/?#working-with-a-local-git-repository
334
565
  */
335
566
  local_backend?: boolean;
567
+ /**
568
+ * Whether to load YAML/JSON CMS configuration file(s)
569
+ */
570
+ load_config_file?: boolean;
336
571
  backend: {
337
572
  /**
338
573
  * The name of the backend to use.
339
574
  */
340
- name: 'git-gateway' | 'github' | 'gitlab' | 'bitbucket' | 'azure' | 'gitea';
575
+ name: 'git-gateway' | 'github' | 'gitlab' | 'bitbucket' | 'azure' | 'gitea' | 'test';
341
576
  /**
342
577
  * [org-or-username]/[repo-name] Required for github, gitlab,
343
578
  * bitbucket, azure, gitea and ignored by git-gateway.
@@ -359,6 +594,10 @@ interface NetlifyCMSConfig {
359
594
  * @default https://api.bitbucket.org/2.0 (Bitbucket)
360
595
  */
361
596
  api_root?: string;
597
+ /**
598
+ * GraphQL API endpoint for the backend
599
+ */
600
+ graphql_api_root?: string;
362
601
  /**
363
602
  * Sets the site_id query param sent to the API endpoint.
364
603
  * Non-Netlify auth setups will often need to set this for
@@ -366,7 +605,7 @@ interface NetlifyCMSConfig {
366
605
  *
367
606
  * @default location.hostname (or cms.netlify.com when on localhost)
368
607
  */
369
- site_doamin?: string;
608
+ site_domain?: string;
370
609
  /**
371
610
  * OAuth client hostname (just the base domain, no path).
372
611
  * Required when using an external OAuth server or self-hosted GitLab.
@@ -375,6 +614,10 @@ interface NetlifyCMSConfig {
375
614
  * @default https://gitlab.com (GitLab)
376
615
  */
377
616
  base_url?: string;
617
+ /**
618
+ * OAuth grant type
619
+ */
620
+ auth_type?: '' | 'pkce';
378
621
  /**
379
622
  * Path to append to base_url for authentication requests.
380
623
  *
@@ -382,12 +625,52 @@ interface NetlifyCMSConfig {
382
625
  * @default oauth/authorize (GitLab)
383
626
  */
384
627
  auth_endpoint?: string;
628
+ /**
629
+ * OAuth application ID
630
+ */
631
+ app_id?: string;
385
632
  /**
386
633
  * Pull (or Merge) Requests label prefix when using editorial workflow.
387
634
  *
388
- * @default decap-cms/
635
+ * @default sveltia-cms/
389
636
  */
390
637
  cms_label_prefix?: string;
638
+ /**
639
+ * Whether to use squash merge for Editorial Workflow
640
+ */
641
+ squash_merges?: boolean;
642
+ /**
643
+ * Deploy preview link context
644
+ */
645
+ preview_context?: string;
646
+ /**
647
+ * Whether to use Open Authoring
648
+ */
649
+ open_authoring?: boolean;
650
+ /**
651
+ * Authentication scope for Open Authoring
652
+ */
653
+ auth_scope?: 'repo' | 'public_repo';
654
+ /**
655
+ * Custom commit messages
656
+ */
657
+ commit_messages?: {
658
+ create?: string;
659
+ update?: string;
660
+ delete?: string;
661
+ uploadMedia?: string;
662
+ deleteMedia?: string;
663
+ openAuthoring?: string;
664
+ };
665
+ /**
666
+ * Whether to enable or disable automatic deployments
667
+ * @deprecated Use skip_ci instead
668
+ */
669
+ automatic_deployments?: boolean;
670
+ /**
671
+ * Whether to enable or disable automatic deployments
672
+ */
673
+ skip_ci?: boolean;
391
674
  };
392
675
  /**
393
676
  * By default, all entries created or edited in the Decap CMS are committed directly
@@ -402,14 +685,14 @@ interface NetlifyCMSConfig {
402
685
  *
403
686
  * @see https://decapcms.org/docs/configuration-options/#publish-mode
404
687
  */
405
- publish_mode?: 'editorial_workflow';
688
+ publish_mode?: '' | 'simple' | 'editorial_workflow';
406
689
  /**
407
690
  * The media_folder option specifies the folder path where
408
691
  * uploaded files should be saved, relative to the base of the repo.
409
692
  *
410
693
  * @example media_folder: "static/images/uploads"
411
694
  */
412
- media_folder: string;
695
+ media_folder?: string;
413
696
  /**
414
697
  * The public_folder option specifies the folder path where the
415
698
  * files uploaded by the media library will be accessed, relative to
@@ -425,11 +708,54 @@ interface NetlifyCMSConfig {
425
708
  * Media library integrations are configured via the media_library property,
426
709
  * and its value should be an object with at least a name property.
427
710
  * A config property can also be used for options that should be passed to the library in use.
711
+ * @deprecated Use media_libraries instead
428
712
  */
429
713
  media_library?: MediaLibrary;
714
+ /**
715
+ * Unified media library option that supports multiple libraries
716
+ */
717
+ media_libraries?: {
718
+ default?: {
719
+ config?: {
720
+ multiple?: boolean;
721
+ max_file_size?: number;
722
+ slugify_filename?: boolean;
723
+ transformations?: Record<string, unknown>;
724
+ };
725
+ };
726
+ cloudinary?: {
727
+ output_filename_only?: boolean;
728
+ use_transformations?: boolean;
729
+ config?: Record<string, unknown>;
730
+ };
731
+ uploadcare?: {
732
+ config?: Record<string, unknown>;
733
+ settings?: {
734
+ autoFilename?: boolean;
735
+ defaultOperations?: string;
736
+ };
737
+ };
738
+ stock_assets?: {
739
+ providers?: ('pexels' | 'pixabay' | 'unsplash')[];
740
+ };
741
+ };
430
742
  site_url?: string;
431
743
  display_url?: string;
744
+ /**
745
+ * @deprecated Use logo.src instead
746
+ */
432
747
  logo_url?: string;
748
+ /**
749
+ * Site logo options
750
+ */
751
+ logo?: {
752
+ src: string;
753
+ show_in_header?: boolean;
754
+ };
755
+ /**
756
+ * URL to redirect users to after logging out
757
+ */
758
+ logout_redirect_url?: string;
433
759
  /**
434
760
  * Define the cms language,
435
761
  *
@@ -453,8 +779,59 @@ interface NetlifyCMSConfig {
453
779
  encoding?: 'unicode' | 'ascii';
454
780
  clean_accents?: boolean;
455
781
  sanitize_replacement?: string;
782
+ trim?: boolean;
783
+ };
784
+ /**
785
+ * Set of collections
786
+ */
787
+ collections?: (FileCollection | FolderCollection | {
788
+ name?: string;
789
+ divider: boolean;
790
+ })[];
791
+ /**
792
+ * Set of singleton files
793
+ */
794
+ singletons?: (FileCollectionEntry | {
795
+ name?: string;
796
+ divider: boolean;
797
+ })[];
798
+ /**
799
+ * Global i18n options
800
+ */
801
+ i18n?: {
802
+ structure: 'single_file' | 'multiple_files' | 'multiple_folders' | 'multiple_folders_i18n_root';
803
+ locales: string[];
804
+ default_locale?: string;
805
+ initial_locales?: 'default' | 'all';
806
+ save_all_locales?: boolean;
807
+ canonical_slug?: {
808
+ key?: string;
809
+ value?: string;
810
+ };
811
+ omit_default_locale_from_filename?: boolean;
812
+ };
813
+ /**
814
+ * Editor view options
815
+ */
816
+ editor?: {
817
+ preview?: boolean;
818
+ };
819
+ /**
820
+ * Data output options
821
+ */
822
+ output?: {
823
+ omit_empty_optional_fields?: boolean;
824
+ encode_file_path?: boolean;
825
+ json?: {
826
+ indent_style?: 'space' | 'tab';
827
+ indent_size?: number;
828
+ };
829
+ yaml?: {
830
+ indent_size?: number;
831
+ indent_sequences?: boolean;
832
+ quote?: 'none' | 'single' | 'double';
833
+ };
456
834
  };
457
- collections: (FileCollection | FolderCollection)[];
458
835
  }
459
836
 
460
837
  declare function defineConfig(config: NetlifyCMSConfig): NetlifyCMSConfig;
@@ -464,10 +841,12 @@ declare const defineFileCollectionEntry: (collection: FileCollectionEntry) => Fi
464
841
  declare function defineBooleanWidget(widget: Omit<BooleanWidget, 'widget'>): BooleanWidget;
465
842
  declare function defineCodeWidget(widget: Omit<CodeWidget, 'widget'>): CodeWidget;
466
843
  declare function defineColorWidget(widget: Omit<ColorWidget, 'widget'>): ColorWidget;
844
+ declare function defineComputeWidget(widget: Omit<ComputeWidget, 'widget'>): ComputeWidget;
467
845
  declare function defineDateTimeWidget(widget: Omit<DateTimeWidget, 'widget'>): DateTimeWidget;
468
846
  declare function defineHiddenWidget(widget: Omit<HiddenWidget, 'widget'>): HiddenWidget;
469
847
  declare function defineFileWidget(widget: Omit<FileWidget, 'widget'>): FileWidget;
470
848
  declare function defineImageWidget(widget: Omit<ImageWidget, 'widget'>): ImageWidget;
849
+ declare function defineKeyValueWidget(widget: Omit<KeyValueWidget, 'widget'>): KeyValueWidget;
471
850
  declare function defineListWidget(widget: Omit<ListWidget, 'widget'>): ListWidget;
472
851
  declare function defineMapWidget(widget: Omit<MapWidget, 'widget'>): MapWidget;
473
852
  declare function defineNumberWidget(widget: Omit<NumberWidget, 'widget'>): NumberWidget;
@@ -477,6 +856,8 @@ declare function defineSelectWidget(widget: Omit<SelectWidget, 'widget'>): Selec
477
856
  declare function defineStringWidget(widget: Omit<StringWidget, 'widget'>): StringWidget;
478
857
  declare function defineTextWidget(widget: Omit<TextWidget, 'widget'>): TextWidget;
479
858
  declare function defineMarkdownWidget(widget: Omit<MarkdownWidget, 'widget'>): MarkdownWidget;
859
+ declare function defineRichTextWidget(widget: Omit<RichTextWidget, 'widget'>): RichTextWidget;
860
+ declare function defineUuidWidget(widget: Omit<UuidWidget, 'widget'>): UuidWidget;
480
861
  interface NetlifyCMSEntry {
481
862
  /**
482
863
  * Name of config file
@@ -516,8 +897,48 @@ interface NetlifyCMSEntry {
516
897
  * @default true
517
898
  */
518
899
  useIdentityWidget?: boolean;
900
+ /**
901
+ * Type of CMS to generate config for
902
+ *
903
+ * @default 'sveltia'
904
+ */
905
+ type?: 'decap' | 'sveltia';
519
906
  }
520
907
  declare function createConfig(root: string, entry?: NetlifyCMSEntry): Promise<void>;
521
908
  declare function export_default(entry?: NetlifyCMSEntry): Promise<Plugin>;
522
909
 
523
- export { type BooleanWidget, type CodeWidget, type Collection, type CollectionField, type ColorWidget, type DateTimeWidget, type FileCollection, type FileCollectionEntry, type FileWidget, type FolderCollection, type HiddenWidget, type ImageWidget, type ListWidget, type MapWidget, type MarkdownWidget, type NetlifyCMSConfig, type NetlifyCMSEntry, type NumberWidget, type ObjectWidget, type RelationWidget, type SelectWidget, type StringWidget, type TextWidget, type Widget, createConfig, export_default as default, defineBooleanWidget, defineCodeWidget, defineColorWidget, defineConfig, defineDateTimeWidget, defineFileCollection, defineFileCollectionEntry, defineFileWidget, defineFolderCollection, defineHiddenWidget, defineImageWidget, defineListWidget, defineMapWidget, defineMarkdownWidget, defineNumberWidget, defineObjectWidget, defineRelationWidget, defineSelectWidget, defineStringWidget, defineTextWidget };
910
+ interface AstroOAuthOptions {
911
+ /**
912
+ * OAuth login route
913
+ * @default '/oauth'
914
+ */
915
+ loginRoute?: string;
916
+ /**
917
+ * OAuth callback route
918
+ * @default '/oauth/callback'
919
+ */
920
+ callbackRoute?: string;
921
+ /**
922
+ * Disable OAuth integration
923
+ * @default false
924
+ */
925
+ disabled?: boolean;
926
+ }
927
+ /**
928
+ * Astro integration for OAuth authentication routes
929
+ * Adds /oauth and /oauth/callback routes for GitHub authentication
930
+ *
931
+ * @example
932
+ * ```ts
933
+ * import { defineConfig } from 'astro/config';
934
+ * import { githubOAuthIntegration } from 'vite-plugin-netlify-cms';
935
+ *
936
+ * export default defineConfig({
937
+ * integrations: [githubOAuthIntegration()],
938
+ * output: 'server', // or 'hybrid'
939
+ * });
940
+ * ```
941
+ */
942
+ declare function githubOAuthIntegration(options?: AstroOAuthOptions): any;
943
+
944
+ export { type AstroOAuthOptions, type BooleanWidget, type CodeWidget, type Collection, type CollectionField, type ColorWidget, type ComputeWidget, type DateTimeWidget, type EditorComponent, type EditorMode, type FileCollection, type FileCollectionEntry, type FileWidget, type FolderCollection, type HiddenWidget, type ImageWidget, type KeyValueWidget, type ListWidget, type MapWidget, type MarkdownButtons, type MarkdownWidget, type NetlifyCMSConfig, type NetlifyCMSEntry, type NumberWidget, type OAuthPluginOptions, type ObjectWidget, type RelationWidget, type RichTextWidget, type SelectWidget, type StringWidget, type TextWidget, type UuidWidget, type VariableFieldType, type Widget, export_default as cmsPlugin, createConfig, export_default as default, defineBooleanWidget, defineCodeWidget, defineColorWidget, defineComputeWidget, defineConfig, defineDateTimeWidget, defineFileCollection, defineFileCollectionEntry, defineFileWidget, defineFolderCollection, defineHiddenWidget, defineImageWidget, defineKeyValueWidget, defineListWidget, defineMapWidget, defineMarkdownWidget, defineNumberWidget, defineObjectWidget, defineRelationWidget, defineRichTextWidget, defineSelectWidget, defineStringWidget, defineTextWidget, defineUuidWidget, githubOAuthIntegration, oauthPlugin };
package/dist/index.js CHANGED
@@ -1,6 +1,133 @@
1
+ // src/oauth/config.ts
2
+ var clientId = process.env.OAUTH_GITHUB_CLIENT_ID;
3
+ var clientSecret = process.env.OAUTH_GITHUB_CLIENT_SECRET;
4
+ var authUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&scope=repo,user`;
5
+ var tokenUrl = "https://github.com/login/oauth/access_token";
6
+
7
+ // src/oauth/callback.ts
8
+ async function handleOAuthCallback(code) {
9
+ const data = {
10
+ code,
11
+ client_id: clientId,
12
+ client_secret: clientSecret
13
+ };
14
+ try {
15
+ const response = await fetch(tokenUrl, {
16
+ method: "POST",
17
+ headers: {
18
+ "Accept": "application/json",
19
+ "Content-Type": "application/json"
20
+ },
21
+ body: JSON.stringify(data)
22
+ });
23
+ if (!response.ok) {
24
+ throw new Error(`HTTP error! status: ${response.status}`);
25
+ }
26
+ const body = await response.json();
27
+ const content = {
28
+ token: body.access_token,
29
+ provider: "github"
30
+ };
31
+ const script = `
32
+ <script>
33
+ const receiveMessage = (message) => {
34
+ window.opener.postMessage(
35
+ 'authorization:${content.provider}:success:${JSON.stringify(content)}',
36
+ message.origin
37
+ );
38
+
39
+ window.removeEventListener("message", receiveMessage, false);
40
+ }
41
+
42
+ window.addEventListener("message", receiveMessage, false);
43
+
44
+ window.opener.postMessage("authorizing:${content.provider}", "*");
45
+ </script>
46
+ `;
47
+ return {
48
+ statusCode: 200,
49
+ headers: {
50
+ "Content-Type": "text/html"
51
+ },
52
+ body: script
53
+ };
54
+ } catch (err) {
55
+ console.error("OAuth callback error:", err);
56
+ return {
57
+ statusCode: 500,
58
+ headers: {
59
+ "Content-Type": "text/html"
60
+ },
61
+ body: "<html><body><h1>OAuth Error</h1><p>Authentication failed. Please close this window and try again.</p></body></html>"
62
+ };
63
+ }
64
+ }
65
+
66
+ // src/oauth/login.ts
67
+ function handleOAuthLogin() {
68
+ return {
69
+ statusCode: 302,
70
+ headers: {
71
+ Location: authUrl
72
+ },
73
+ body: ""
74
+ };
75
+ }
76
+
77
+ // src/oauth-plugin.ts
78
+ function oauthPlugin(options) {
79
+ const {
80
+ loginRoute = "/oauth",
81
+ callbackRoute = "/oauth/callback",
82
+ disabled = false
83
+ } = options ?? {};
84
+ if (disabled) {
85
+ return {
86
+ name: "vite-plugin-oauth"
87
+ };
88
+ }
89
+ let warningShown = false;
90
+ return {
91
+ name: "vite-plugin-oauth",
92
+ configureServer(server) {
93
+ server.middlewares.use(async (req, res, next) => {
94
+ const url = req.url;
95
+ if (!warningShown) {
96
+ if (!clientId || !clientSecret) {
97
+ console.warn(
98
+ "\x1B[33m\u26A0 OAuth plugin enabled but OAUTH_GITHUB_CLIENT_ID or OAUTH_GITHUB_CLIENT_SECRET environment variables are not set.\x1B[0m"
99
+ );
100
+ }
101
+ warningShown = true;
102
+ }
103
+ if (url === loginRoute) {
104
+ const result = handleOAuthLogin();
105
+ res.writeHead(result.statusCode, result.headers);
106
+ res.end(result.body);
107
+ return;
108
+ }
109
+ if (url?.startsWith(callbackRoute)) {
110
+ const urlParams = new URLSearchParams(url.split("?")[1]);
111
+ const code = urlParams.get("code");
112
+ if (!code) {
113
+ res.writeHead(400, { "Content-Type": "text/html" });
114
+ res.end("<html><body><h1>Error</h1><p>No code provided</p></body></html>");
115
+ return;
116
+ }
117
+ const result = await handleOAuthCallback(code);
118
+ res.writeHead(result.statusCode, result.headers);
119
+ res.end(result.body);
120
+ return;
121
+ }
122
+ next();
123
+ });
124
+ }
125
+ };
126
+ }
127
+
1
128
  // src/plugin.ts
2
- import { mkdir, readdir, writeFile } from "node:fs/promises";
3
- import path from "node:path";
129
+ import { mkdir, readdir, writeFile } from "fs/promises";
130
+ import path from "path";
4
131
  import { loadConfigFromFile } from "vite";
5
132
  import YAML from "yaml";
6
133
 
@@ -15,8 +142,7 @@ var index_template_default = `<!DOCTYPE html>
15
142
  {{ identity }}
16
143
  </head>
17
144
  <body>
18
- <!-- Include the script that builds the page and powers Decap CMS -->
19
- <script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>
145
+ {{ script }}
20
146
  </body>
21
147
  </html>`;
22
148
 
@@ -47,6 +173,12 @@ function defineColorWidget(widget) {
47
173
  ...widget
48
174
  };
49
175
  }
176
+ function defineComputeWidget(widget) {
177
+ return {
178
+ widget: "compute",
179
+ ...widget
180
+ };
181
+ }
50
182
  function defineDateTimeWidget(widget) {
51
183
  return { widget: "datetime", ...widget };
52
184
  }
@@ -68,6 +200,12 @@ function defineImageWidget(widget) {
68
200
  ...widget
69
201
  };
70
202
  }
203
+ function defineKeyValueWidget(widget) {
204
+ return {
205
+ widget: "keyvalue",
206
+ ...widget
207
+ };
208
+ }
71
209
  function defineListWidget(widget) {
72
210
  return {
73
211
  widget: "list",
@@ -119,6 +257,18 @@ function defineMarkdownWidget(widget) {
119
257
  ...widget
120
258
  };
121
259
  }
260
+ function defineRichTextWidget(widget) {
261
+ return {
262
+ widget: "richtext",
263
+ ...widget
264
+ };
265
+ }
266
+ function defineUuidWidget(widget) {
267
+ return {
268
+ widget: "uuid",
269
+ ...widget
270
+ };
271
+ }
122
272
  async function createFolderIfNotExists(path2) {
123
273
  try {
124
274
  await readdir(path2);
@@ -159,10 +309,11 @@ async function getConfigFile(root, configFile) {
159
309
  throw new Error(`Config file not found`);
160
310
  }
161
311
  }
162
- function createIndex(title, iconUrl, useIdentityWidget) {
312
+ function createIndex(title, iconUrl, useIdentityWidget, type = "sveltia") {
163
313
  const icon = iconUrl ? `<link rel="icon" type="image/svg+xml" href="${iconUrl}" />` : "";
164
314
  const identity = useIdentityWidget ? `<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>` : "";
165
- const document = index_template_default.replace("{{ title }}", title).replace("{{ icon }}", icon).replace("{{ identity }}", identity);
315
+ const script = type === "decap" ? `<script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>` : `<script src="https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js"></script>`;
316
+ const document = index_template_default.replace("{{ script }}", script).replace("{{ title }}", title).replace("{{ icon }}", icon).replace("{{ identity }}", identity);
166
317
  return document;
167
318
  }
168
319
  async function createConfig(root, entry) {
@@ -173,7 +324,8 @@ async function createConfig(root, entry) {
173
324
  createIndexHTML = true,
174
325
  title = "Admin",
175
326
  iconUrl = "https://decapcms.org/img/decap-logo.svg",
176
- useIdentityWidget = true
327
+ useIdentityWidget = false,
328
+ type = "sveltia"
177
329
  } = entry ?? {};
178
330
  const resolvedConfig = config ?? await getConfigFile(root, configFile);
179
331
  await createFolderIfNotExists(path.join(root, saveFolder));
@@ -181,7 +333,7 @@ async function createConfig(root, entry) {
181
333
  await saveConfig(document, path.join(root, saveFolder, "config.yml"));
182
334
  if (!createIndexHTML)
183
335
  return;
184
- const indexHTML = createIndex(title, iconUrl, useIdentityWidget);
336
+ const indexHTML = createIndex(title, iconUrl, useIdentityWidget, type);
185
337
  await saveConfig(indexHTML, path.join(root, saveFolder, "index.html"));
186
338
  }
187
339
  async function plugin_default(entry) {
@@ -210,14 +362,49 @@ async function plugin_default(entry) {
210
362
  };
211
363
  }
212
364
 
365
+ // src/astro-oauth-integration.ts
366
+ var defaultOptions = {
367
+ loginRoute: "/oauth",
368
+ callbackRoute: "/oauth/callback",
369
+ disabled: false
370
+ };
371
+ function githubOAuthIntegration(options) {
372
+ const { loginRoute, callbackRoute, disabled } = { ...defaultOptions, ...options };
373
+ if (!loginRoute?.startsWith("/") || !callbackRoute?.startsWith("/")) {
374
+ throw new Error('`loginRoute` and `callbackRoute` options must start with "/"');
375
+ }
376
+ return {
377
+ name: "github-oauth-integration",
378
+ hooks: {
379
+ "astro:config:setup": async ({ injectRoute }) => {
380
+ if (disabled) {
381
+ return;
382
+ }
383
+ injectRoute({
384
+ pattern: loginRoute,
385
+ entrypoint: "vite-plugin-netlify-cms/src/oauth/astro-login.ts",
386
+ prerender: false
387
+ });
388
+ injectRoute({
389
+ pattern: callbackRoute,
390
+ entrypoint: "vite-plugin-netlify-cms/src/oauth/astro-callback.ts",
391
+ prerender: false
392
+ });
393
+ }
394
+ }
395
+ };
396
+ }
397
+
213
398
  // index.ts
214
- var vite_plugin_netlify_cms_default = plugin_default;
399
+ var index_default = plugin_default;
215
400
  export {
401
+ plugin_default as cmsPlugin,
216
402
  createConfig,
217
- vite_plugin_netlify_cms_default as default,
403
+ index_default as default,
218
404
  defineBooleanWidget,
219
405
  defineCodeWidget,
220
406
  defineColorWidget,
407
+ defineComputeWidget,
221
408
  defineConfig,
222
409
  defineDateTimeWidget,
223
410
  defineFileCollection,
@@ -226,14 +413,19 @@ export {
226
413
  defineFolderCollection,
227
414
  defineHiddenWidget,
228
415
  defineImageWidget,
416
+ defineKeyValueWidget,
229
417
  defineListWidget,
230
418
  defineMapWidget,
231
419
  defineMarkdownWidget,
232
420
  defineNumberWidget,
233
421
  defineObjectWidget,
234
422
  defineRelationWidget,
423
+ defineRichTextWidget,
235
424
  defineSelectWidget,
236
425
  defineStringWidget,
237
- defineTextWidget
426
+ defineTextWidget,
427
+ defineUuidWidget,
428
+ githubOAuthIntegration,
429
+ oauthPlugin
238
430
  };
239
431
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/plugin.ts","../src/index.template.ts","../index.ts"],"sourcesContent":["import { mkdir, readdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { Plugin } from 'vite';\nimport { loadConfigFromFile } from 'vite';\nimport YAML from 'yaml';\nimport indexHTMLTemplate from './index.template';\nimport type {\n BooleanWidget,\n CodeWidget,\n ColorWidget,\n DateTimeWidget,\n FileCollection,\n FileCollectionEntry,\n FileWidget,\n FolderCollection,\n HiddenWidget,\n ImageWidget,\n ListWidget,\n MapWidget,\n MarkdownWidget,\n NetlifyCMSConfig,\n NumberWidget,\n ObjectWidget,\n RelationWidget,\n SelectWidget,\n StringWidget,\n TextWidget,\n} from './types';\n\nexport function defineConfig(config: NetlifyCMSConfig): NetlifyCMSConfig {\n return config;\n}\nexport function defineFolderCollection(collection: FolderCollection) {\n return collection;\n}\nexport const defineFileCollection = (collection: FileCollection) => collection;\nexport const defineFileCollectionEntry = (collection: FileCollectionEntry) => collection;\nexport function defineBooleanWidget(widget: Omit<BooleanWidget, 'widget'>): BooleanWidget {\n return {\n widget: 'boolean',\n ...widget,\n };\n}\nexport function defineCodeWidget(widget: Omit<CodeWidget, 'widget'>): CodeWidget {\n return {\n widget: 'code',\n ...widget,\n };\n}\nexport function defineColorWidget(widget: Omit<ColorWidget, 'widget'>): ColorWidget {\n return {\n widget: 'color',\n ...widget,\n };\n}\nexport function defineDateTimeWidget(widget: Omit<DateTimeWidget, 'widget'>): DateTimeWidget {\n return { widget: 'datetime', ...widget };\n}\nexport function defineHiddenWidget(widget: Omit<HiddenWidget, 'widget'>): HiddenWidget {\n return {\n widget: 'hidden',\n ...widget,\n };\n}\nexport function defineFileWidget(widget: Omit<FileWidget, 'widget'>): FileWidget {\n return {\n widget: 'file',\n ...widget,\n };\n}\nexport function defineImageWidget(widget: Omit<ImageWidget, 'widget'>): ImageWidget {\n return {\n widget: 'image',\n ...widget,\n };\n}\nexport function defineListWidget(widget: Omit<ListWidget, 'widget'>): ListWidget {\n return {\n widget: 'list',\n ...widget,\n };\n}\nexport function defineMapWidget(widget: Omit<MapWidget, 'widget'>): MapWidget {\n return {\n widget: 'map',\n ...widget,\n };\n}\nexport function defineNumberWidget(widget: Omit<NumberWidget, 'widget'>): NumberWidget {\n return {\n widget: 'number',\n ...widget,\n };\n}\nexport function defineObjectWidget(widget: Omit<ObjectWidget, 'widget'>): ObjectWidget {\n return {\n widget: 'object',\n ...widget,\n };\n}\nexport function defineRelationWidget(widget: Omit<RelationWidget, 'widget'>): RelationWidget {\n return { widget: 'relation', ...widget };\n}\nexport function defineSelectWidget(widget: Omit<SelectWidget, 'widget'>): SelectWidget {\n return {\n widget: 'select',\n ...widget,\n };\n}\nexport function defineStringWidget(widget: Omit<StringWidget, 'widget'>): StringWidget {\n return {\n widget: 'string',\n ...widget,\n };\n}\nexport function defineTextWidget(widget: Omit<TextWidget, 'widget'>): TextWidget {\n return {\n widget: 'text',\n ...widget,\n };\n}\nexport function defineMarkdownWidget(widget: Omit<MarkdownWidget, 'widget'>): MarkdownWidget {\n return {\n widget: 'markdown',\n ...widget,\n };\n}\n\nexport interface NetlifyCMSEntry {\n /**\n * Name of config file\n *\n * @default cms.config\n */\n configFile?: string;\n\n /**\n * Netlify CMS config object\n */\n config?: NetlifyCMSConfig;\n\n /**\n * Folder to save config file\n *\n * @default ./public/admin\n */\n saveFolder?: string;\n\n /**\n * If has to create index.html file in the save folder\n */\n createIndexHTML?: boolean;\n\n /**\n * Title of the admin page\n *\n * @default Admin\n */\n title?: string;\n\n /**\n * Icon URL of the admin page\n *\n * @default https://decapcms.org/img/decap-logo.svg\n */\n iconUrl?: string;\n\n /**\n * If has to use identity widget\n *\n * @default true\n */\n useIdentityWidget?: boolean;\n}\n\nasync function createFolderIfNotExists(path: string) {\n try {\n await readdir(path);\n }\n catch {\n await mkdir(path, { recursive: true });\n }\n}\n\nasync function saveConfig(document: string, pathTo: string) {\n await writeFile(pathTo, document);\n}\n\nfunction resolveConfigFilePath(configFile: string) {\n const _path = configFile.startsWith('.') ? configFile.slice(2) : configFile;\n\n if (!_path)\n return configFile;\n if (['ts', 'js', 'cjs', 'mjs'].some(ext => _path.includes(ext)))\n return _path.split('.').slice(0, -1).join('.');\n\n return _path;\n}\n\nasync function getConfigFile(root: string, configFile: string): Promise<NetlifyCMSConfig> {\n try {\n const files = await readdir(root);\n const configPath = resolveConfigFilePath(configFile);\n\n if (configPath.includes('/')) {\n const [folder, file] = configPath.split('/');\n return await getConfigFile(path.join(root, folder), file);\n }\n\n const file = files.find(file => file.startsWith(configPath));\n\n if (!file)\n throw new Error(`Config file not found`);\n\n const { config }\n = (await loadConfigFromFile(\n { command: 'build', mode: '' },\n path.join(root, file),\n )) ?? {};\n\n if (!config)\n throw new Error(`Config file not found`);\n\n return config as NetlifyCMSConfig;\n }\n catch {\n throw new Error(`Config file not found`);\n }\n}\n\nfunction createIndex(title: string, iconUrl: string, useIdentityWidget: boolean) {\n const icon = iconUrl ? `<link rel=\"icon\" type=\"image/svg+xml\" href=\"${iconUrl}\" />` : '';\n const identity = useIdentityWidget ? `<script src=\"https://identity.netlify.com/v1/netlify-identity-widget.js\"></script>` : '';\n\n const document = indexHTMLTemplate\n .replace('{{ title }}', title)\n .replace('{{ icon }}', icon)\n .replace('{{ identity }}', identity);\n\n return document;\n}\n\nexport async function createConfig(root: string, entry?: NetlifyCMSEntry) {\n const {\n configFile = 'cms.config',\n config,\n saveFolder = './public/admin',\n createIndexHTML = true,\n title = 'Admin',\n iconUrl = 'https://decapcms.org/img/decap-logo.svg',\n useIdentityWidget = true,\n } = entry ?? {};\n\n const resolvedConfig = config ?? (await getConfigFile(root, configFile));\n await createFolderIfNotExists(path.join(root, saveFolder));\n\n const document = YAML.stringify(resolvedConfig);\n await saveConfig(document, path.join(root, saveFolder, 'config.yml'));\n\n if (!createIndexHTML)\n return;\n\n const indexHTML = createIndex(title, iconUrl, useIdentityWidget);\n await saveConfig(indexHTML, path.join(root, saveFolder, 'index.html'));\n}\n\nexport default async function (entry?: NetlifyCMSEntry): Promise<Plugin> {\n let root = '';\n\n return {\n name: 'vite-plugin-netlify-cms',\n configResolved: (config) => {\n root = config.root;\n },\n buildStart: async () => {\n try {\n await createConfig(root, entry);\n }\n catch (error) {\n console.log(error);\n }\n },\n handleHotUpdate: async ({ file }) => {\n if (file.includes(entry?.configFile ?? 'cms.config')) {\n try {\n await createConfig(root, entry);\n }\n catch (error) {\n console.log(error);\n }\n }\n },\n };\n}\n","export default `<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>{{ title }}</title>\n {{ icon }}\n {{ identity }}\n </head>\n <body>\n <!-- Include the script that builds the page and powers Decap CMS -->\n <script src=\"https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js\"></script>\n </body>\n</html>`\n","export * from \"./src/plugin.js\";\nexport * from \"./src/types.js\";\nimport plugin from \"./src/plugin.js\";\n\nexport default plugin;\n"],"mappings":"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","names":["path","file"]}
1
+ {"version":3,"sources":["../src/oauth/config.ts","../src/oauth/callback.ts","../src/oauth/login.ts","../src/oauth-plugin.ts","../src/plugin.ts","../src/index.template.ts","../src/astro-oauth-integration.ts","../index.ts"],"sourcesContent":["export const clientId = process.env.OAUTH_GITHUB_CLIENT_ID;\nexport const clientSecret = process.env.OAUTH_GITHUB_CLIENT_SECRET;\n\nexport const authUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&scope=repo,user`;\nexport const tokenUrl = 'https://github.com/login/oauth/access_token';\n","import { clientId, clientSecret, tokenUrl } from './config';\n\n/**\n * OAuth callback handler - exchanges code for access token\n */\nexport async function handleOAuthCallback(code: string) {\n const data = {\n code,\n client_id: clientId,\n client_secret: clientSecret,\n };\n\n try {\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Accept': 'application/json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(data),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const body = await response.json();\n\n const content = {\n token: body.access_token,\n provider: 'github',\n };\n\n // Return HTML with postMessage script to communicate with CMS\n const script = `\n <script>\n const receiveMessage = (message) => {\n window.opener.postMessage(\n 'authorization:${content.provider}:success:${JSON.stringify(content)}',\n message.origin\n );\n\n window.removeEventListener(\"message\", receiveMessage, false);\n }\n\n window.addEventListener(\"message\", receiveMessage, false);\n\n window.opener.postMessage(\"authorizing:${content.provider}\", \"*\");\n </script>\n `;\n\n return {\n statusCode: 200,\n headers: {\n 'Content-Type': 'text/html',\n },\n body: script,\n };\n }\n catch (err) {\n console.error('OAuth callback error:', err);\n return {\n statusCode: 500,\n headers: {\n 'Content-Type': 'text/html',\n },\n body: '<html><body><h1>OAuth Error</h1><p>Authentication failed. Please close this window and try again.</p></body></html>',\n };\n }\n}\n","import { authUrl } from './config';\n\n/**\n * OAuth login handler - redirects to GitHub authorization\n */\nexport function handleOAuthLogin() {\n return {\n statusCode: 302,\n headers: {\n Location: authUrl,\n },\n body: '',\n };\n}\n","import type { Plugin } from 'vite';\nimport { handleOAuthCallback } from './oauth/callback';\nimport { clientId, clientSecret } from './oauth/config';\nimport { handleOAuthLogin } from './oauth/login';\n\nexport interface OAuthPluginOptions {\n /**\n * OAuth login route\n * @default '/oauth'\n */\n loginRoute?: string;\n\n /**\n * OAuth callback route\n * @default '/oauth/callback'\n */\n callbackRoute?: string;\n\n /**\n * Disable OAuth plugin\n * @default false\n */\n disabled?: boolean;\n}\n\n/**\n * Vite plugin for OAuth authentication routes\n * Adds /oauth and /oauth/callback routes for GitHub authentication\n */\nexport default function oauthPlugin(options?: OAuthPluginOptions): Plugin {\n const {\n loginRoute = '/oauth',\n callbackRoute = '/oauth/callback',\n disabled = false,\n } = options ?? {};\n\n if (disabled) {\n return {\n name: 'vite-plugin-oauth',\n };\n }\n\n let warningShown = false;\n\n return {\n name: 'vite-plugin-oauth',\n configureServer(server) {\n server.middlewares.use(async (req, res, next) => {\n const url = req.url;\n\n // Show warning once if environment variables are not set\n if (!warningShown) {\n if (!clientId || !clientSecret) {\n console.warn(\n '\\x1B[33m⚠ OAuth plugin enabled but OAUTH_GITHUB_CLIENT_ID or OAUTH_GITHUB_CLIENT_SECRET environment variables are not set.\\x1B[0m',\n );\n }\n warningShown = true;\n }\n\n // Handle OAuth login route\n if (url === loginRoute) {\n const result = handleOAuthLogin();\n res.writeHead(result.statusCode, result.headers);\n res.end(result.body);\n return;\n }\n\n // Handle OAuth callback route\n if (url?.startsWith(callbackRoute)) {\n const urlParams = new URLSearchParams(url.split('?')[1]);\n const code = urlParams.get('code');\n\n if (!code) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end('<html><body><h1>Error</h1><p>No code provided</p></body></html>');\n return;\n }\n\n const result = await handleOAuthCallback(code);\n res.writeHead(result.statusCode, result.headers);\n res.end(result.body);\n return;\n }\n\n next();\n });\n },\n };\n}\n","import type { Plugin } from 'vite';\nimport type {\n BooleanWidget,\n CodeWidget,\n ColorWidget,\n ComputeWidget,\n DateTimeWidget,\n FileCollection,\n FileCollectionEntry,\n FileWidget,\n FolderCollection,\n HiddenWidget,\n ImageWidget,\n KeyValueWidget,\n ListWidget,\n MapWidget,\n MarkdownWidget,\n NetlifyCMSConfig,\n NumberWidget,\n ObjectWidget,\n RelationWidget,\n RichTextWidget,\n SelectWidget,\n StringWidget,\n TextWidget,\n UuidWidget,\n} from './types';\nimport { mkdir, readdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { loadConfigFromFile } from 'vite';\nimport YAML from 'yaml';\nimport indexHTMLTemplate from './index.template';\n\nexport function defineConfig(config: NetlifyCMSConfig): NetlifyCMSConfig {\n return config;\n}\nexport function defineFolderCollection(collection: FolderCollection) {\n return collection;\n}\nexport const defineFileCollection = (collection: FileCollection) => collection;\nexport const defineFileCollectionEntry = (collection: FileCollectionEntry) => collection;\nexport function defineBooleanWidget(widget: Omit<BooleanWidget, 'widget'>): BooleanWidget {\n return {\n widget: 'boolean',\n ...widget,\n };\n}\nexport function defineCodeWidget(widget: Omit<CodeWidget, 'widget'>): CodeWidget {\n return {\n widget: 'code',\n ...widget,\n };\n}\nexport function defineColorWidget(widget: Omit<ColorWidget, 'widget'>): ColorWidget {\n return {\n widget: 'color',\n ...widget,\n };\n}\nexport function defineComputeWidget(widget: Omit<ComputeWidget, 'widget'>): ComputeWidget {\n return {\n widget: 'compute',\n ...widget,\n };\n}\nexport function defineDateTimeWidget(widget: Omit<DateTimeWidget, 'widget'>): DateTimeWidget {\n return { widget: 'datetime', ...widget };\n}\nexport function defineHiddenWidget(widget: Omit<HiddenWidget, 'widget'>): HiddenWidget {\n return {\n widget: 'hidden',\n ...widget,\n };\n}\nexport function defineFileWidget(widget: Omit<FileWidget, 'widget'>): FileWidget {\n return {\n widget: 'file',\n ...widget,\n };\n}\nexport function defineImageWidget(widget: Omit<ImageWidget, 'widget'>): ImageWidget {\n return {\n widget: 'image',\n ...widget,\n };\n}\nexport function defineKeyValueWidget(widget: Omit<KeyValueWidget, 'widget'>): KeyValueWidget {\n return {\n widget: 'keyvalue',\n ...widget,\n };\n}\nexport function defineListWidget(widget: Omit<ListWidget, 'widget'>): ListWidget {\n return {\n widget: 'list',\n ...widget,\n };\n}\nexport function defineMapWidget(widget: Omit<MapWidget, 'widget'>): MapWidget {\n return {\n widget: 'map',\n ...widget,\n };\n}\nexport function defineNumberWidget(widget: Omit<NumberWidget, 'widget'>): NumberWidget {\n return {\n widget: 'number',\n ...widget,\n };\n}\nexport function defineObjectWidget(widget: Omit<ObjectWidget, 'widget'>): ObjectWidget {\n return {\n widget: 'object',\n ...widget,\n };\n}\nexport function defineRelationWidget(widget: Omit<RelationWidget, 'widget'>): RelationWidget {\n return { widget: 'relation', ...widget };\n}\nexport function defineSelectWidget(widget: Omit<SelectWidget, 'widget'>): SelectWidget {\n return {\n widget: 'select',\n ...widget,\n };\n}\nexport function defineStringWidget(widget: Omit<StringWidget, 'widget'>): StringWidget {\n return {\n widget: 'string',\n ...widget,\n };\n}\nexport function defineTextWidget(widget: Omit<TextWidget, 'widget'>): TextWidget {\n return {\n widget: 'text',\n ...widget,\n };\n}\nexport function defineMarkdownWidget(widget: Omit<MarkdownWidget, 'widget'>): MarkdownWidget {\n return {\n widget: 'markdown',\n ...widget,\n };\n}\nexport function defineRichTextWidget(widget: Omit<RichTextWidget, 'widget'>): RichTextWidget {\n return {\n widget: 'richtext',\n ...widget,\n };\n}\nexport function defineUuidWidget(widget: Omit<UuidWidget, 'widget'>): UuidWidget {\n return {\n widget: 'uuid',\n ...widget,\n };\n}\n\nexport interface NetlifyCMSEntry {\n /**\n * Name of config file\n *\n * @default cms.config\n */\n configFile?: string;\n\n /**\n * Netlify CMS config object\n */\n config?: NetlifyCMSConfig;\n\n /**\n * Folder to save config file\n *\n * @default ./public/admin\n */\n saveFolder?: string;\n\n /**\n * If has to create index.html file in the save folder\n */\n createIndexHTML?: boolean;\n\n /**\n * Title of the admin page\n *\n * @default Admin\n */\n title?: string;\n\n /**\n * Icon URL of the admin page\n *\n * @default https://decapcms.org/img/decap-logo.svg\n */\n iconUrl?: string;\n\n /**\n * If has to use identity widget\n *\n * @default true\n */\n useIdentityWidget?: boolean;\n\n /**\n * Type of CMS to generate config for\n *\n * @default 'sveltia'\n */\n type?: 'decap' | 'sveltia';\n}\n\nasync function createFolderIfNotExists(path: string) {\n try {\n await readdir(path);\n }\n catch {\n await mkdir(path, { recursive: true });\n }\n}\n\nasync function saveConfig(document: string, pathTo: string) {\n await writeFile(pathTo, document);\n}\n\nfunction resolveConfigFilePath(configFile: string) {\n const _path = configFile.startsWith('.') ? configFile.slice(2) : configFile;\n\n if (!_path)\n return configFile;\n if (['ts', 'js', 'cjs', 'mjs'].some(ext => _path.includes(ext)))\n return _path.split('.').slice(0, -1).join('.');\n\n return _path;\n}\n\nasync function getConfigFile(root: string, configFile: string): Promise<NetlifyCMSConfig> {\n try {\n const files = await readdir(root);\n const configPath = resolveConfigFilePath(configFile);\n\n if (configPath.includes('/')) {\n const [folder, file] = configPath.split('/');\n return await getConfigFile(path.join(root, folder), file);\n }\n\n const file = files.find(file => file.startsWith(configPath));\n\n if (!file)\n throw new Error(`Config file not found`);\n\n const { config }\n = (await loadConfigFromFile(\n { command: 'build', mode: '' },\n path.join(root, file),\n )) ?? {};\n\n if (!config)\n throw new Error(`Config file not found`);\n\n return config as NetlifyCMSConfig;\n }\n catch {\n throw new Error(`Config file not found`);\n }\n}\n\nfunction createIndex(title: string, iconUrl: string, useIdentityWidget: boolean, type: 'decap' | 'sveltia' = 'sveltia') {\n const icon = iconUrl ? `<link rel=\"icon\" type=\"image/svg+xml\" href=\"${iconUrl}\" />` : '';\n const identity = useIdentityWidget ? `<script src=\"https://identity.netlify.com/v1/netlify-identity-widget.js\"></script>` : '';\n\n const script = type === 'decap'\n ? `<script src=\"https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js\"></script>`\n : `<script src=\"https://unpkg.com/@sveltia/cms/dist/sveltia-cms.js\"></script>`;\n\n const document = indexHTMLTemplate\n .replace('{{ script }}', script)\n .replace('{{ title }}', title)\n .replace('{{ icon }}', icon)\n .replace('{{ identity }}', identity);\n\n return document;\n}\n\nexport async function createConfig(root: string, entry?: NetlifyCMSEntry) {\n const {\n configFile = 'cms.config',\n config,\n saveFolder = './public/admin',\n createIndexHTML = true,\n title = 'Admin',\n iconUrl = 'https://decapcms.org/img/decap-logo.svg',\n useIdentityWidget = false,\n type = 'sveltia',\n } = entry ?? {};\n\n const resolvedConfig = config ?? (await getConfigFile(root, configFile));\n await createFolderIfNotExists(path.join(root, saveFolder));\n\n const document = YAML.stringify(resolvedConfig);\n await saveConfig(document, path.join(root, saveFolder, 'config.yml'));\n\n if (!createIndexHTML)\n return;\n\n const indexHTML = createIndex(title, iconUrl, useIdentityWidget, type);\n await saveConfig(indexHTML, path.join(root, saveFolder, 'index.html'));\n}\n\nexport default async function (entry?: NetlifyCMSEntry): Promise<Plugin> {\n let root = '';\n\n return {\n name: 'vite-plugin-netlify-cms',\n configResolved: (config) => {\n root = config.root;\n },\n buildStart: async () => {\n try {\n await createConfig(root, entry);\n }\n catch (error) {\n console.log(error);\n }\n },\n handleHotUpdate: async ({ file }) => {\n if (file.includes(entry?.configFile ?? 'cms.config')) {\n try {\n await createConfig(root, entry);\n }\n catch (error) {\n console.log(error);\n }\n }\n },\n };\n}\n","export default `<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>{{ title }}</title>\n {{ icon }}\n {{ identity }}\n </head>\n <body>\n {{ script }}\n </body>\n</html>`;\n","export interface AstroOAuthOptions {\n /**\n * OAuth login route\n * @default '/oauth'\n */\n loginRoute?: string;\n\n /**\n * OAuth callback route\n * @default '/oauth/callback'\n */\n callbackRoute?: string;\n\n /**\n * Disable OAuth integration\n * @default false\n */\n disabled?: boolean;\n}\n\nconst defaultOptions: AstroOAuthOptions = {\n loginRoute: '/oauth',\n callbackRoute: '/oauth/callback',\n disabled: false,\n};\n\n/**\n * Astro integration for OAuth authentication routes\n * Adds /oauth and /oauth/callback routes for GitHub authentication\n *\n * @example\n * ```ts\n * import { defineConfig } from 'astro/config';\n * import { githubOAuthIntegration } from 'vite-plugin-netlify-cms';\n *\n * export default defineConfig({\n * integrations: [githubOAuthIntegration()],\n * output: 'server', // or 'hybrid'\n * });\n * ```\n */\nexport function githubOAuthIntegration(options?: AstroOAuthOptions): any {\n const { loginRoute, callbackRoute, disabled } = { ...defaultOptions, ...options };\n\n if (!loginRoute?.startsWith('/') || !callbackRoute?.startsWith('/')) {\n throw new Error('`loginRoute` and `callbackRoute` options must start with \"/\"');\n }\n\n return {\n name: 'github-oauth-integration',\n hooks: {\n 'astro:config:setup': async ({ injectRoute }: any) => {\n if (disabled) {\n return;\n }\n\n // Inject OAuth login route\n injectRoute({\n pattern: loginRoute,\n entrypoint: 'vite-plugin-netlify-cms/src/oauth/astro-login.ts',\n prerender: false,\n });\n\n // Inject OAuth callback route\n injectRoute({\n pattern: callbackRoute,\n entrypoint: 'vite-plugin-netlify-cms/src/oauth/astro-callback.ts',\n prerender: false,\n });\n },\n },\n };\n}\n","import oauthPlugin from './src/oauth-plugin.js';\nimport cmsPlugin from './src/plugin.js';\n\nexport * from './src/astro-oauth-integration.js';\nexport * from './src/oauth-plugin.js';\nexport * from './src/plugin.js';\nexport * from './src/types.js';\n\nexport { cmsPlugin, oauthPlugin };\nexport default cmsPlugin;\n"],"mappings":"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","names":["path","file"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@caiquecamargo/vite-plugin-netlify-cms",
3
3
  "type": "module",
4
- "version": "0.0.17",
4
+ "version": "0.1.0",
5
5
  "author": "Caique de Camargo",
6
6
  "license": "ISC",
7
7
  "repository": {
@@ -23,17 +23,17 @@
23
23
  "dist"
24
24
  ],
25
25
  "peerDependencies": {
26
- "vite": "^4.0.0 || ^5.0.0"
26
+ "vite": "^7.0.0"
27
27
  },
28
28
  "dependencies": {
29
- "yaml": "^2.3.4"
29
+ "yaml": "^2.8.2"
30
30
  },
31
31
  "devDependencies": {
32
- "@antfu/eslint-config": "^2.12.1",
33
- "@types/node": "^20.10.5",
34
- "eslint": "^8.57.0",
35
- "tsup": "^8.0.1",
36
- "typescript": "^5.3.3"
32
+ "@antfu/eslint-config": "^6.7.1",
33
+ "@types/node": "^20.19.27",
34
+ "eslint": "^9.39.2",
35
+ "tsup": "^8.5.1",
36
+ "typescript": "^5.9.3"
37
37
  },
38
38
  "publishConfig": {
39
39
  "access": "public",