@rathnasgala/cli 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rathnasgala/cli",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src"
@@ -1,7 +1,7 @@
1
1
  import { lstat, readFile, rename, rm, writeFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { normalizeSiteConfigurationOptions } from '@rathnasgala/content-validation';
4
- import { parse, stringify } from 'yaml';
4
+ import { parseDocument } from 'yaml';
5
5
 
6
6
  import { scaffoldOptionNames } from './scaffold-options.js';
7
7
 
@@ -25,14 +25,18 @@ export async function configureSite(root, designOptions) {
25
25
  }
26
26
 
27
27
  let config;
28
+ let document;
28
29
  try {
29
- config = parse(await readFile(configPath, 'utf8'));
30
+ document = parseDocument(await readFile(configPath, 'utf8'));
31
+ if (document.errors.length > 0) throw document.errors[0];
32
+ config = document.toJS();
30
33
  } catch (error) {
31
34
  throw new TypeError(`Invalid site.config.yml: ${error.message}`);
32
35
  }
33
36
  if (config.schemaVersion !== 1 || config.design == null || Array.isArray(config.design)) {
34
37
  throw new TypeError('Unsupported site configuration schema');
35
38
  }
39
+ if (Object.keys(designOptions).length === 0) return config;
36
40
 
37
41
  const siteOptions = Object.fromEntries(
38
42
  Object.entries(designOptions).filter(([name]) => !scaffoldOptionNames.includes(name))
@@ -42,25 +46,38 @@ export async function configureSite(root, designOptions) {
42
46
  for (const [name, value] of Object.entries(designOptions)) {
43
47
  if (scaffoldOptionNames.includes(name)) {
44
48
  config.design[name] = nonEmptyString(value, `Design option ${name}`);
49
+ document.setIn(['design', name], config.design[name]);
45
50
  }
46
51
  }
47
- if (normalizedSiteOptions.siteName != null) config.site.name = normalizedSiteOptions.siteName;
48
- if (normalizedSiteOptions.siteAuthor != null) config.site.author = normalizedSiteOptions.siteAuthor;
52
+ if (normalizedSiteOptions.siteName != null) {
53
+ config.site.name = normalizedSiteOptions.siteName;
54
+ document.setIn(['site', 'name'], config.site.name);
55
+ }
56
+ if (normalizedSiteOptions.siteAuthor != null) {
57
+ config.site.author = normalizedSiteOptions.siteAuthor;
58
+ document.setIn(['site', 'author'], config.site.author);
59
+ }
49
60
  if (normalizedSiteOptions.defaultLanguage != null) {
50
61
  config.site.defaultLanguage = normalizedSiteOptions.defaultLanguage;
62
+ document.setIn(['site', 'defaultLanguage'], config.site.defaultLanguage);
63
+ }
64
+ if (normalizedSiteOptions.timezone != null) {
65
+ config.site.timezone = normalizedSiteOptions.timezone;
66
+ document.setIn(['site', 'timezone'], config.site.timezone);
51
67
  }
52
- if (normalizedSiteOptions.timezone != null) config.site.timezone = normalizedSiteOptions.timezone;
53
68
  if (normalizedSiteOptions.shareTargets != null) {
54
69
  config.sharing.targets = normalizedSiteOptions.shareTargets;
70
+ document.setIn(['sharing', 'targets'], config.sharing.targets);
55
71
  }
56
72
  if (normalizedSiteOptions.socialProfiles != null) {
57
73
  config.sharing.socialProfiles = normalizedSiteOptions.socialProfiles;
74
+ document.setIn(['sharing', 'socialProfiles'], config.sharing.socialProfiles);
58
75
  }
59
76
 
60
77
  const temporary = `${configPath}.gala-config-${process.pid}`;
61
78
  const backup = `${configPath}.gala-backup-${process.pid}`;
62
79
  try {
63
- await writeFile(temporary, stringify(config), { flag: 'wx' });
80
+ await writeFile(temporary, String(document), { flag: 'wx' });
64
81
  await rename(configPath, backup);
65
82
  try {
66
83
  await rename(temporary, configPath);
@@ -45,6 +45,7 @@ export function setRepositoryOrigin({ root, owner, repository, spawnProcess = sp
45
45
 
46
46
  export function verifyRepositoryOrigin({ root, owner, repository, spawnProcess = spawn }) {
47
47
  const expected = `https://github.com/${owner}/${repository}.git`;
48
+ const expectedPath = `/${owner}/${repository}.git`;
48
49
  return new Promise((resolve, reject) => {
49
50
  const child = spawnProcess('git', ['-C', root, 'remote', 'get-url', 'origin'], {
50
51
  cwd: root, shell: false, stdio: ['ignore', 'pipe', 'inherit']
@@ -56,8 +57,19 @@ export function verifyRepositoryOrigin({ root, owner, repository, spawnProcess =
56
57
  child.once('exit', (code, signal) => {
57
58
  if (signal) reject(new Error(`Git origin verification terminated by signal ${signal}`));
58
59
  else if (code !== 0) reject(new Error(`Git origin verification exited with code ${code}`));
59
- else if (output.trim() !== expected) reject(new Error(`Existing checkout origin must be ${expected}`));
60
- else resolve(root);
60
+ else {
61
+ let origin;
62
+ try {
63
+ origin = new URL(output.trim());
64
+ } catch {
65
+ reject(new Error(`Existing checkout origin must be ${expected}`));
66
+ return;
67
+ }
68
+ if (origin.protocol !== 'https:' || origin.hostname !== 'github.com' || origin.port !== ''
69
+ || origin.pathname !== expectedPath || origin.search !== '' || origin.hash !== '') {
70
+ reject(new Error(`Existing checkout origin must be ${expected}`));
71
+ } else resolve(root);
72
+ }
61
73
  });
62
74
  });
63
75
  }