diff --git a/.gitignore b/.gitignore index 800bdf3..a37001a 100644 --- a/.gitignore +++ b/.gitignore @@ -145,3 +145,7 @@ dist ## Dev site's built files dev-site/cms + +# Private script files +scripts/.config/config-dev.yml +cms-lock.json diff --git a/package.json b/package.json index 4304834..e0c0e60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "snow-cms", - "version": "1.0.3", + "version": "1.0.4", "description": "A configurable CMS built with Svelte.", "keywords": [], "author": "Sammy-T", @@ -20,6 +20,7 @@ "serve:site": "vite dev-site", "build": "vite build -c cms/vite.config.js", "build:watch:cms": "vite build --minify false --emptyOutDir -w -c cms/vite.config.js --outDir ../dev-site/cms", + "prebuild:watch:cms": "node scripts/copy-config.js", "prepublishOnly": "npm run build" }, "dependencies": { @@ -38,6 +39,7 @@ "@sveltejs/vite-plugin-svelte": "^3.1.1", "browser-fs-access": "^0.35.0", "dayjs": "^1.11.11", + "dotenv": "^16.4.5", "events": "^3.3.0", "js-yaml": "^4.1.0", "npm-run-all": "^4.1.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1046d36..0d2eda9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,6 +48,9 @@ importers: dayjs: specifier: ^1.11.11 version: 1.11.11 + dotenv: + specifier: ^16.4.5 + version: 16.4.5 events: specifier: ^3.3.0 version: 3.3.0 @@ -581,6 +584,10 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} @@ -1824,6 +1831,8 @@ snapshots: dependencies: dequal: 2.0.3 + dotenv@16.4.5: {} + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 diff --git a/scripts/.config/config-prod.yml b/scripts/.config/config-prod.yml new file mode 100644 index 0000000..d9ecd3b --- /dev/null +++ b/scripts/.config/config-prod.yml @@ -0,0 +1,49 @@ +backend: + name: custom # Can be 'github' or 'custom' + branch: master + file: 'backend/example.js' # The script to use as a custom backend. Relative to the /cms-config path + # repo: owner-name/repo-name + # app_name: 'My GitHub App Name' # The GitHub app used with the 'github' backend + # client_id: my-client-id # The client id of the GitHub app + # api_root: http://localhost:8788 # The root of the api used to exchange the github token + # auth_endpoint: /api/github/oauth/token # The api endpoint to exchange the auth code for a github token +local_backend: true # Set to 'true' to override the backend with the local backend +custom_actions: 'cms-actions.js' # Configure to include custom actions. Relative to the /cms-config path + +repo_folder: snow-cms # The root folder of the repository +media_folder: 'dev-site/img/uploads' # The repo's media folder +public_folder: '/img/uploads' # The site's media path (Media location when served online or via dev server) + +collections: + - name: 'news' # Used in routes, e.g., /admin/collections/blog + label: 'News' # Used in the UI + folder: 'dev-site/content/news' # The repo path to the folder where documents are stored + create: true # Allow users to create new documents in this collection + slug: '{{year}}{{month}}{{day}}-{{slug}}' # Filename template, e.g., YYYY-MM-DD-title + extension: 'md' # Only 'md' is supported currently + fields: # The fields for each document, usually in front matter + - { label: 'Title', name: 'title', widget: 'string' } + - { label: 'Publish Date', name: 'date', widget: 'datetime', type: 'date', + date_format: 'MM.DD.YYYY', time_format: 'HH:mm', datetime_format: 'MM.DD.YYYY HH:mm' } + - { label: 'Draft', name: 'draft', widget: 'boolean', default: true, required: false } + - { label: 'Body', name: 'body', widget: 'markdown' } + + - name: 'blog' # Used in routes, e.g., /admin/collections/blog + label: 'Blog' # Used in the UI + folder: 'dev-site/content/blog' # The repo path to the folder where documents are stored + create: true # Allow users to create new documents in this collection + slug: '{{year}}{{month}}{{day}}-{{slug}}' # Filename template, e.g., YYYY-MM-DD-title + extension: 'md' # Only 'md' is supported currently + fields: # The fields for each document, usually in front matter + - { label: 'Title', name: 'title', widget: 'string' } + - { label: 'Publish Date', name: 'date', widget: 'datetime', type: 'date', + date_format: 'MM.DD.YYYY', time_format: 'HH:mm', datetime_format: 'MM.DD.YYYY HH:mm' } + - { label: 'Draft', name: 'draft', widget: 'boolean', default: true, required: false } + - { label: 'Body', name: 'body', widget: 'markdown' } + +previews: # Filenames in this section are relative to the /cms-config path + default: main # Default preview name, can also be specified on each collection using a 'preview' attribute + templates: + - { name: 'main', template: 'preview-template.html' } + styles: # Styles will load in listed order + - { name: 'main', files: ['preview-styles.css', 'preview-styles-news.css'] } diff --git a/scripts/copy-config.js b/scripts/copy-config.js new file mode 100644 index 0000000..8a7e996 --- /dev/null +++ b/scripts/copy-config.js @@ -0,0 +1,54 @@ +import 'dotenv/config'; +import { copyFile, readFile, writeFile } from 'fs/promises'; + +const lockfileName = 'cms-lock.json'; +const lockfilePath = new URL(`../${lockfileName}`, import.meta.url); + +async function readLockfile() { + try { + const contents = await readFile(lockfilePath, { encoding: 'utf8' }); + + return JSON.parse(contents); + } catch(error) { + console.warn('No lockfile found.', error); + return; + } +} + +async function copyConfig() { + try { + // Get the CMS env. + // Fall back to 'prod' if not found. + const cmsEnv = process.env.CMS_ENV ?? 'prod'; + + // Check the custom lockfile + const lock = await readLockfile(); + + // If the lockfile matches the specified env, return early. + if(lock?.cms_env === cmsEnv) { + console.log('CMS lockfile matches.'); + return; + } + + // The env and lockfile don't match + // so copy the specified config. + const sourcePath = `scripts/.config/config-${cmsEnv}.yml`; + const configPath = 'dev-site/cms-config/config.yml'; + + await copyFile(sourcePath, configPath); + console.log(`CMS env '${cmsEnv}'.\nCopied ${sourcePath} => ${configPath}`); + + // Update the lock data + const newLock = { ...lock }; + newLock.cms_env = cmsEnv; + + const data = JSON.stringify(newLock, null, 2); + + // Update the lockfile + await writeFile(lockfileName, data); + } catch(error) { + console.error('CMS copy config error', error); + } +} + +copyConfig();