@knocklabs/cli 0.3.0 → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +505 -162
  2. package/dist/commands/branch/create.js +0 -2
  3. package/dist/commands/branch/delete.js +4 -3
  4. package/dist/commands/branch/exit.js +0 -2
  5. package/dist/commands/branch/list.js +0 -2
  6. package/dist/commands/branch/merge.js +82 -0
  7. package/dist/commands/branch/switch.js +0 -2
  8. package/dist/commands/channel/list.js +73 -0
  9. package/dist/commands/environment/list.js +73 -0
  10. package/dist/commands/guide/new.js +276 -0
  11. package/dist/commands/guide/pull.js +5 -6
  12. package/dist/commands/guide/push.js +1 -1
  13. package/dist/commands/guide/validate.js +1 -1
  14. package/dist/commands/init.js +108 -0
  15. package/dist/commands/layout/new.js +228 -0
  16. package/dist/commands/layout/pull.js +5 -6
  17. package/dist/commands/layout/push.js +1 -1
  18. package/dist/commands/layout/validate.js +1 -1
  19. package/dist/commands/message-type/new.js +228 -0
  20. package/dist/commands/message-type/pull.js +5 -6
  21. package/dist/commands/message-type/push.js +1 -1
  22. package/dist/commands/message-type/validate.js +1 -1
  23. package/dist/commands/partial/new.js +274 -0
  24. package/dist/commands/partial/pull.js +5 -6
  25. package/dist/commands/partial/push.js +1 -1
  26. package/dist/commands/partial/validate.js +1 -1
  27. package/dist/commands/pull.js +7 -2
  28. package/dist/commands/push.js +6 -4
  29. package/dist/commands/translation/pull.js +1 -1
  30. package/dist/commands/translation/push.js +1 -1
  31. package/dist/commands/translation/validate.js +1 -1
  32. package/dist/commands/workflow/new.js +179 -54
  33. package/dist/commands/workflow/pull.js +6 -8
  34. package/dist/commands/workflow/push.js +1 -1
  35. package/dist/commands/workflow/validate.js +1 -1
  36. package/dist/lib/api-v1.js +23 -2
  37. package/dist/lib/auth.js +1 -1
  38. package/dist/lib/base-command.js +18 -15
  39. package/dist/lib/helpers/flag.js +0 -2
  40. package/dist/lib/helpers/project-config.js +158 -0
  41. package/dist/lib/helpers/request.js +1 -2
  42. package/dist/lib/helpers/string.js +4 -4
  43. package/dist/lib/helpers/typegen.js +1 -1
  44. package/dist/lib/marshal/email-layout/generator.js +152 -0
  45. package/dist/lib/marshal/email-layout/helpers.js +6 -9
  46. package/dist/lib/marshal/email-layout/index.js +1 -0
  47. package/dist/lib/marshal/email-layout/writer.js +15 -3
  48. package/dist/lib/marshal/guide/generator.js +163 -0
  49. package/dist/lib/marshal/guide/helpers.js +6 -10
  50. package/dist/lib/marshal/guide/index.js +1 -0
  51. package/dist/lib/marshal/guide/writer.js +5 -9
  52. package/dist/lib/marshal/message-type/generator.js +139 -0
  53. package/dist/lib/marshal/message-type/helpers.js +6 -10
  54. package/dist/lib/marshal/message-type/index.js +1 -0
  55. package/dist/lib/marshal/message-type/writer.js +5 -1
  56. package/dist/lib/marshal/partial/generator.js +159 -0
  57. package/dist/lib/marshal/partial/helpers.js +6 -10
  58. package/dist/lib/marshal/partial/index.js +1 -0
  59. package/dist/lib/marshal/partial/writer.js +3 -0
  60. package/dist/lib/marshal/translation/helpers.js +6 -10
  61. package/dist/lib/marshal/translation/processor.isomorphic.js +4 -4
  62. package/dist/lib/marshal/translation/writer.js +2 -2
  63. package/dist/lib/marshal/workflow/generator.js +175 -19
  64. package/dist/lib/marshal/workflow/helpers.js +7 -10
  65. package/dist/lib/run-context/loader.js +5 -0
  66. package/dist/lib/templates.js +131 -0
  67. package/oclif.manifest.json +826 -266
  68. package/package.json +14 -9
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get PROJECT_CONFIG_FILE_NAME () {
13
+ return PROJECT_CONFIG_FILE_NAME;
14
+ },
15
+ get ResourceDirectoriesByType () {
16
+ return ResourceDirectoriesByType;
17
+ },
18
+ get findAndReadProjectConfig () {
19
+ return findAndReadProjectConfig;
20
+ },
21
+ get findProjectConfig () {
22
+ return findProjectConfig;
23
+ },
24
+ get readProjectConfig () {
25
+ return readProjectConfig;
26
+ },
27
+ get resolveKnockDir () {
28
+ return resolveKnockDir;
29
+ },
30
+ get resolveResourceDir () {
31
+ return resolveResourceDir;
32
+ }
33
+ });
34
+ const _nodepath = /*#__PURE__*/ _interop_require_wildcard(require("node:path"));
35
+ const _findup = /*#__PURE__*/ _interop_require_default(require("find-up"));
36
+ const _fsextra = /*#__PURE__*/ _interop_require_wildcard(require("fs-extra"));
37
+ const _zod = require("zod");
38
+ const _fs = require("./fs");
39
+ function _interop_require_default(obj) {
40
+ return obj && obj.__esModule ? obj : {
41
+ default: obj
42
+ };
43
+ }
44
+ function _getRequireWildcardCache(nodeInterop) {
45
+ if (typeof WeakMap !== "function") return null;
46
+ var cacheBabelInterop = new WeakMap();
47
+ var cacheNodeInterop = new WeakMap();
48
+ return (_getRequireWildcardCache = function(nodeInterop) {
49
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
50
+ })(nodeInterop);
51
+ }
52
+ function _interop_require_wildcard(obj, nodeInterop) {
53
+ if (!nodeInterop && obj && obj.__esModule) {
54
+ return obj;
55
+ }
56
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
57
+ return {
58
+ default: obj
59
+ };
60
+ }
61
+ var cache = _getRequireWildcardCache(nodeInterop);
62
+ if (cache && cache.has(obj)) {
63
+ return cache.get(obj);
64
+ }
65
+ var newObj = {
66
+ __proto__: null
67
+ };
68
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
69
+ for(var key in obj){
70
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
71
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
72
+ if (desc && (desc.get || desc.set)) {
73
+ Object.defineProperty(newObj, key, desc);
74
+ } else {
75
+ newObj[key] = obj[key];
76
+ }
77
+ }
78
+ }
79
+ newObj.default = obj;
80
+ if (cache) {
81
+ cache.set(obj, newObj);
82
+ }
83
+ return newObj;
84
+ }
85
+ const PROJECT_CONFIG_FILE_NAME = "knock.json";
86
+ /**
87
+ * Schema for the project configuration file.
88
+ */ const projectConfigSchema = _zod.z.object({
89
+ knockDir: _zod.z.string()
90
+ });
91
+ const findProjectConfig = async ()=>{
92
+ const configPath = await (0, _findup.default)(PROJECT_CONFIG_FILE_NAME);
93
+ return configPath;
94
+ };
95
+ const readProjectConfig = async (configPath)=>{
96
+ const rawConfig = await _fsextra.readJSON(configPath);
97
+ const config = projectConfigSchema.parse(rawConfig);
98
+ return config;
99
+ };
100
+ const resolveKnockDir = async (knockDirFlag, projectConfig)=>{
101
+ if (knockDirFlag) {
102
+ return knockDirFlag;
103
+ }
104
+ if (!projectConfig) {
105
+ return undefined;
106
+ }
107
+ // Project configuration exists, must is invalid. So we error out.
108
+ if ([
109
+ "",
110
+ undefined,
111
+ null
112
+ ].includes(projectConfig.knockDir)) {
113
+ throw new Error("No knock directory specified in the project configuration");
114
+ }
115
+ const abspath = _nodepath.isAbsolute(projectConfig.knockDir) ? projectConfig.knockDir : _nodepath.resolve(process.cwd(), projectConfig.knockDir);
116
+ const exists = await _fsextra.pathExists(abspath);
117
+ if (!exists || !await (0, _fs.isDirectory)(abspath)) {
118
+ throw new Error(`${projectConfig.knockDir} does not exist or is not a directory`);
119
+ }
120
+ return {
121
+ abspath,
122
+ exists
123
+ };
124
+ };
125
+ const findAndReadProjectConfig = async ()=>{
126
+ const configPath = await findProjectConfig();
127
+ if (!configPath) {
128
+ return undefined;
129
+ }
130
+ return readProjectConfig(configPath);
131
+ };
132
+ const ResourceDirectoriesByType = {
133
+ workflow: "workflows",
134
+ guide: "guides",
135
+ partial: "partials",
136
+ email_layout: "layouts",
137
+ message_type: "message-types",
138
+ translation: "translations"
139
+ };
140
+ const resolveResourceDir = async (projectConfig, resourceType, basePath = process.cwd())=>{
141
+ if (!projectConfig) {
142
+ return {
143
+ abspath: basePath,
144
+ exists: true
145
+ };
146
+ }
147
+ const absoluteKnockDir = _nodepath.isAbsolute(projectConfig.knockDir) ? projectConfig.knockDir : _nodepath.resolve(basePath, projectConfig.knockDir);
148
+ const resourceDir = ResourceDirectoriesByType[resourceType];
149
+ const absResourceDirPath = _nodepath.resolve(absoluteKnockDir, resourceDir);
150
+ const exists = await _fsextra.pathExists(absResourceDirPath);
151
+ if (exists && !await (0, _fs.isDirectory)(absResourceDirPath)) {
152
+ throw new Error(`${absResourceDirPath} is not a directory`);
153
+ }
154
+ return {
155
+ abspath: absResourceDirPath,
156
+ exists
157
+ };
158
+ };
@@ -49,14 +49,13 @@ const formatErrorRespMessage = ({ status, data })=>{
49
49
  return message;
50
50
  };
51
51
  const formatMgmtError = (apiError)=>{
52
+ var _apiError_error_message, _apiError_error_errors;
52
53
  if (apiError.status === 500) {
53
54
  return "An internal server error occurred";
54
55
  }
55
- var _apiError_error_message;
56
56
  // Prefer the error message from the error object over
57
57
  // the error message formatted by the Stainless SDK
58
58
  const description = `${(_apiError_error_message = apiError.error.message) !== null && _apiError_error_message !== void 0 ? _apiError_error_message : apiError.message} (status: ${apiError.status})`;
59
- var _apiError_error_errors;
60
59
  const inputErrors = (_apiError_error_errors = apiError.error.errors) !== null && _apiError_error_errors !== void 0 ? _apiError_error_errors : [];
61
60
  if (Array.isArray(inputErrors) && inputErrors.length > 0) {
62
61
  const errs = inputErrors.map((e)=>new _error.JsonDataError(e.message, e.field));
@@ -1,6 +1,4 @@
1
- /*
2
- * Checks if a given string is in a slugified format.
3
- */ "use strict";
1
+ "use strict";
4
2
  Object.defineProperty(exports, "__esModule", {
5
3
  value: true
6
4
  });
@@ -21,7 +19,9 @@ _export(exports, {
21
19
  return slugify;
22
20
  }
23
21
  });
24
- const SLUG_FORMAT_RE = /^[\w-]+$/;
22
+ /*
23
+ * Checks if a given string is in a slugified format.
24
+ */ const SLUG_FORMAT_RE = /^[\w-]+$/;
25
25
  const SLUG_LOWERCASE_FORMAT_RE = /^[\d_a-z-]+$/;
26
26
  const checkSlugifiedFormat = (input, opts = {})=>{
27
27
  const { onlyLowerCase = true } = opts;
@@ -44,10 +44,10 @@ function getLanguageFromExtension(extension) {
44
44
  }
45
45
  }
46
46
  function transformSchema(schema) {
47
+ var _schema_properties;
47
48
  if (schema.type === "object" && !schema.additionalProperties) {
48
49
  schema.additionalProperties = false;
49
50
  }
50
- var _schema_properties;
51
51
  for (const key of Object.keys((_schema_properties = schema.properties) !== null && _schema_properties !== void 0 ? _schema_properties : {})){
52
52
  const property = schema.properties[key];
53
53
  if (property.type === "object") {
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get generateEmailLayoutDir () {
13
+ return generateEmailLayoutDir;
14
+ },
15
+ get generateEmailLayoutFromTemplate () {
16
+ return generateEmailLayoutFromTemplate;
17
+ },
18
+ get scaffoldEmailLayoutDirBundle () {
19
+ return scaffoldEmailLayoutDirBundle;
20
+ },
21
+ get validateEmailLayoutKey () {
22
+ return validateEmailLayoutKey;
23
+ }
24
+ });
25
+ const _lodash = require("lodash");
26
+ const _string = require("../../helpers/string");
27
+ const _templates = /*#__PURE__*/ _interop_require_wildcard(require("../../templates"));
28
+ const _constisomorphic = require("../shared/const.isomorphic");
29
+ const _processorisomorphic = require("./processor.isomorphic");
30
+ const _reader = require("./reader");
31
+ const _writer = require("./writer");
32
+ function _getRequireWildcardCache(nodeInterop) {
33
+ if (typeof WeakMap !== "function") return null;
34
+ var cacheBabelInterop = new WeakMap();
35
+ var cacheNodeInterop = new WeakMap();
36
+ return (_getRequireWildcardCache = function(nodeInterop) {
37
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
38
+ })(nodeInterop);
39
+ }
40
+ function _interop_require_wildcard(obj, nodeInterop) {
41
+ if (!nodeInterop && obj && obj.__esModule) {
42
+ return obj;
43
+ }
44
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
45
+ return {
46
+ default: obj
47
+ };
48
+ }
49
+ var cache = _getRequireWildcardCache(nodeInterop);
50
+ if (cache && cache.has(obj)) {
51
+ return cache.get(obj);
52
+ }
53
+ var newObj = {
54
+ __proto__: null
55
+ };
56
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
57
+ for(var key in obj){
58
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
59
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
60
+ if (desc && (desc.get || desc.set)) {
61
+ Object.defineProperty(newObj, key, desc);
62
+ } else {
63
+ newObj[key] = obj[key];
64
+ }
65
+ }
66
+ }
67
+ newObj.default = obj;
68
+ if (cache) {
69
+ cache.set(obj, newObj);
70
+ }
71
+ return newObj;
72
+ }
73
+ const validateEmailLayoutKey = (input)=>{
74
+ if (!(0, _string.checkSlugifiedFormat)(input, {
75
+ onlyLowerCase: true
76
+ })) {
77
+ return "must include only lowercase alphanumeric, dash, or underscore characters";
78
+ }
79
+ return undefined;
80
+ };
81
+ /*
82
+ * Returns the default scaffolded HTML layout content.
83
+ */ const defaultHtmlLayoutContent = ()=>{
84
+ return `<!DOCTYPE html>
85
+ <html>
86
+ <head>
87
+ <meta charset="utf-8">
88
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
89
+ <title>{{ title }}</title>
90
+ </head>
91
+ <body>
92
+ <div style="max-width: 600px; margin: 0 auto; padding: 20px;">
93
+ {{ content }}
94
+ </div>
95
+ </body>
96
+ </html>`;
97
+ };
98
+ /*
99
+ * Returns the default scaffolded text layout content.
100
+ */ const defaultTextLayoutContent = ()=>{
101
+ return `{{ content }}`;
102
+ };
103
+ /*
104
+ * Scaffolds a new email layout directory bundle with default content.
105
+ */ const scaffoldEmailLayoutDirBundle = (attrs)=>{
106
+ const htmlLayoutFilePath = "html_layout.html";
107
+ const textLayoutFilePath = "text_layout.txt";
108
+ const defaultHtmlContent = defaultHtmlLayoutContent();
109
+ const defaultTextContent = defaultTextLayoutContent();
110
+ const layoutJson = {
111
+ name: attrs.name,
112
+ [`html_layout${_constisomorphic.FILEPATH_MARKER}`]: htmlLayoutFilePath,
113
+ [`text_layout${_constisomorphic.FILEPATH_MARKER}`]: textLayoutFilePath
114
+ };
115
+ return {
116
+ [_processorisomorphic.LAYOUT_JSON]: layoutJson,
117
+ [htmlLayoutFilePath]: defaultHtmlContent,
118
+ [textLayoutFilePath]: defaultTextContent
119
+ };
120
+ };
121
+ const generateEmailLayoutDir = async (emailLayoutDirCtx, attrs)=>{
122
+ const bundle = scaffoldEmailLayoutDirBundle(attrs);
123
+ return (0, _writer.writeEmailLayoutDirFromBundle)(emailLayoutDirCtx, bundle);
124
+ };
125
+ const generateEmailLayoutFromTemplate = async (emailLayoutDirCtx, templateString, attrs)=>{
126
+ let tempDir;
127
+ try {
128
+ // Download the template directory into a temp directory
129
+ tempDir = await _templates.downloadTemplate(templateString);
130
+ // Create an email layout directory context for the temp directory
131
+ const tempEmailLayoutDirCtx = {
132
+ type: "email_layout",
133
+ key: "temp",
134
+ abspath: tempDir,
135
+ exists: true
136
+ };
137
+ // Read the layout.json from the temp directory we downloaded
138
+ const [emailLayout, errors] = await (0, _reader.readEmailLayoutDir)(tempEmailLayoutDirCtx, {
139
+ withExtractedFiles: true
140
+ });
141
+ if (errors.length > 0 || !emailLayout) {
142
+ throw new Error(`Invalid email layout template: ${errors.join(", ")}`);
143
+ }
144
+ // Modify the email layout data with the new attributes
145
+ const emailLayoutData = (0, _lodash.cloneDeep)(emailLayout);
146
+ emailLayoutData.name = attrs.name;
147
+ // Finally, we write the email layout into the target email layout directory
148
+ await (0, _writer.writeEmailLayoutDirFromData)(emailLayoutDirCtx, emailLayoutData);
149
+ } finally{
150
+ await _templates.cleanupTempDir(tempDir);
151
+ }
152
+ };
@@ -25,6 +25,7 @@ _export(exports, {
25
25
  const _nodepath = /*#__PURE__*/ _interop_require_wildcard(require("node:path"));
26
26
  const _core = require("@oclif/core");
27
27
  const _fsextra = /*#__PURE__*/ _interop_require_wildcard(require("fs-extra"));
28
+ const _projectconfig = require("../../helpers/project-config");
28
29
  const _processorisomorphic = require("./processor.isomorphic");
29
30
  function _getRequireWildcardCache(nodeInterop) {
30
31
  if (typeof WeakMap !== "function") return null;
@@ -74,7 +75,7 @@ const lsEmailLayoutJson = async (dirPath)=>{
74
75
  const exists = await _fsextra.pathExists(emailLayoutJsonPath);
75
76
  return exists ? emailLayoutJsonPath : undefined;
76
77
  };
77
- const ensureValidCommandTarget = async (props, runContext)=>{
78
+ const ensureValidCommandTarget = async (props, runContext, projectConfig)=>{
78
79
  const { args, flags } = props;
79
80
  const { commandId, resourceDir: resourceDirCtx, cwd: runCwd } = runContext;
80
81
  // If the target resource is a different type than the current resource dir
@@ -86,19 +87,15 @@ const ensureValidCommandTarget = async (props, runContext)=>{
86
87
  if (flags.all && args.emailLayoutKey) {
87
88
  return _core.ux.error(`emailLayoutKey arg \`${args.emailLayoutKey}\` cannot also be provided when using --all`);
88
89
  }
90
+ // Default to knock project config first if present, otherwise cwd.
91
+ const layoutsIndexDirCtx = await (0, _projectconfig.resolveResourceDir)(projectConfig, "email_layout", runCwd);
89
92
  // --all flag is given, which means no layout key arg.
90
93
  if (flags.all) {
91
94
  // If --all flag used inside a layout directory, then require a layouts dir path.
92
95
  if (resourceDirCtx && !flags["layouts-dir"]) {
93
96
  return _core.ux.error("Missing required flag layouts-dir");
94
97
  }
95
- // Targeting all layout dirs in the layouts index dir.
96
- // TODO: Default to the knock project config first if present before cwd.
97
- const defaultToCwd = {
98
- abspath: runCwd,
99
- exists: true
100
- };
101
- const indexDirCtx = flags["layouts-dir"] || defaultToCwd;
98
+ const indexDirCtx = flags["layouts-dir"] || layoutsIndexDirCtx;
102
99
  return {
103
100
  type: "emailLayoutsIndexDir",
104
101
  context: indexDirCtx
@@ -109,7 +106,7 @@ const ensureValidCommandTarget = async (props, runContext)=>{
109
106
  if (resourceDirCtx && resourceDirCtx.key !== args.emailLayoutKey) {
110
107
  return _core.ux.error(`Cannot run ${commandId} \`${args.emailLayoutKey}\` inside another layout directory:\n${resourceDirCtx.key}`);
111
108
  }
112
- const targetDirPath = resourceDirCtx ? resourceDirCtx.abspath : _nodepath.resolve(runCwd, args.emailLayoutKey);
109
+ const targetDirPath = resourceDirCtx ? resourceDirCtx.abspath : _nodepath.resolve(layoutsIndexDirCtx.abspath, args.emailLayoutKey);
113
110
  const layoutDirCtx = {
114
111
  type: "email_layout",
115
112
  key: args.emailLayoutKey,
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
+ _export_star(require("./generator"), exports);
5
6
  _export_star(require("./helpers"), exports);
6
7
  _export_star(require("./processor.isomorphic"), exports);
7
8
  _export_star(require("./reader"), exports);
@@ -12,6 +12,9 @@ _export(exports, {
12
12
  get pruneLayoutsIndexDir () {
13
13
  return pruneLayoutsIndexDir;
14
14
  },
15
+ get writeEmailLayoutDirFromBundle () {
16
+ return writeEmailLayoutDirFromBundle;
17
+ },
15
18
  get writeEmailLayoutDirFromData () {
16
19
  return writeEmailLayoutDirFromData;
17
20
  },
@@ -82,18 +85,27 @@ const writeEmailLayoutDirFromData = async (emailLayoutDirCtx, remoteEmailLayout,
82
85
  withExtractedFiles: true
83
86
  }) : [];
84
87
  const bundle = (0, _processorisomorphic.buildEmailLayoutDirBundle)(remoteEmailLayout, localEmailLayout, withSchema ? EMAIL_LAYOUT_SCHEMA : undefined);
88
+ return writeEmailLayoutDirFromBundle(emailLayoutDirCtx, bundle);
89
+ };
90
+ /*
91
+ * A lower level write function that takes a constructed email layout dir bundle
92
+ * and writes it into a layout directory on a local file system.
93
+ *
94
+ * It does not make any assumptions about how the email layout directory bundle was
95
+ * built; for example, it can be from parsing the layout data fetched from
96
+ * the Knock API, or built manually for scaffolding purposes.
97
+ */ const writeEmailLayoutDirFromBundle = async (emailLayoutDirCtx, emailLayoutDirBundle)=>{
85
98
  const backupDirPath = _nodepath.default.resolve(_const.sandboxDir, (0, _lodash.uniqueId)("backup"));
86
99
  try {
87
- // We store a backup in case there's an error.
88
100
  if (emailLayoutDirCtx.exists) {
89
101
  await _fsextra.copy(emailLayoutDirCtx.abspath, backupDirPath);
90
102
  await _fsextra.emptyDir(emailLayoutDirCtx.abspath);
91
103
  }
92
- const promises = Object.entries(bundle).map(([relpath, fileContent])=>{
104
+ const promises = Object.entries(emailLayoutDirBundle).map(([relpath, fileContent])=>{
93
105
  const filePath = _nodepath.default.resolve(emailLayoutDirCtx.abspath, relpath);
94
106
  return relpath === _processorisomorphic.LAYOUT_JSON ? _fsextra.outputJson(filePath, fileContent, {
95
107
  spaces: _json.DOUBLE_SPACES
96
- }) : _fsextra.outputFile(filePath, fileContent);
108
+ }) : _fsextra.outputFile(filePath, fileContent !== null && fileContent !== void 0 ? fileContent : "");
97
109
  });
98
110
  await Promise.all(promises);
99
111
  } catch (error) {
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get generateGuideDir () {
13
+ return generateGuideDir;
14
+ },
15
+ get generateGuideFromTemplate () {
16
+ return generateGuideFromTemplate;
17
+ },
18
+ get scaffoldGuideDirBundle () {
19
+ return scaffoldGuideDirBundle;
20
+ },
21
+ get scaffoldValuesFromSchema () {
22
+ return scaffoldValuesFromSchema;
23
+ },
24
+ get validateGuideKey () {
25
+ return validateGuideKey;
26
+ }
27
+ });
28
+ const _string = require("../../helpers/string");
29
+ const _templates = /*#__PURE__*/ _interop_require_wildcard(require("../../templates"));
30
+ const _processorisomorphic = require("./processor.isomorphic");
31
+ const _reader = require("./reader");
32
+ const _writer = require("./writer");
33
+ function _getRequireWildcardCache(nodeInterop) {
34
+ if (typeof WeakMap !== "function") return null;
35
+ var cacheBabelInterop = new WeakMap();
36
+ var cacheNodeInterop = new WeakMap();
37
+ return (_getRequireWildcardCache = function(nodeInterop) {
38
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
39
+ })(nodeInterop);
40
+ }
41
+ function _interop_require_wildcard(obj, nodeInterop) {
42
+ if (!nodeInterop && obj && obj.__esModule) {
43
+ return obj;
44
+ }
45
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
46
+ return {
47
+ default: obj
48
+ };
49
+ }
50
+ var cache = _getRequireWildcardCache(nodeInterop);
51
+ if (cache && cache.has(obj)) {
52
+ return cache.get(obj);
53
+ }
54
+ var newObj = {
55
+ __proto__: null
56
+ };
57
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
58
+ for(var key in obj){
59
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
60
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
61
+ if (desc && (desc.get || desc.set)) {
62
+ Object.defineProperty(newObj, key, desc);
63
+ } else {
64
+ newObj[key] = obj[key];
65
+ }
66
+ }
67
+ }
68
+ newObj.default = obj;
69
+ if (cache) {
70
+ cache.set(obj, newObj);
71
+ }
72
+ return newObj;
73
+ }
74
+ const validateGuideKey = (input)=>{
75
+ if (!(0, _string.checkSlugifiedFormat)(input, {
76
+ onlyLowerCase: true
77
+ })) {
78
+ return "must include only lowercase alphanumeric, dash, or underscore characters";
79
+ }
80
+ return undefined;
81
+ };
82
+ const scaffoldValuesFromSchema = (fields)=>{
83
+ const values = {};
84
+ for (const field of fields){
85
+ switch(field.type){
86
+ case "text":
87
+ case "textarea":
88
+ case "markdown":
89
+ values[field.key] = "";
90
+ break;
91
+ case "boolean":
92
+ values[field.key] = false;
93
+ break;
94
+ case "select":
95
+ case "multi_select":
96
+ values[field.key] = null;
97
+ break;
98
+ case "button":
99
+ values[field.key] = {
100
+ text: "",
101
+ action: ""
102
+ };
103
+ break;
104
+ }
105
+ }
106
+ return values;
107
+ };
108
+ const scaffoldGuideDirBundle = (attrs)=>{
109
+ const { name, messageType, variantKey = "default" } = attrs;
110
+ // Find the selected variant or default to the first one
111
+ const variant = messageType.variants.find((v)=>v.key === variantKey) || messageType.variants[0];
112
+ if (!variant) {
113
+ throw new Error(`Message type ${messageType.key} has no variants available`);
114
+ }
115
+ // Scaffold values from the variant's field schema
116
+ const values = scaffoldValuesFromSchema(variant.fields);
117
+ const guideJson = {
118
+ name,
119
+ steps: [
120
+ {
121
+ ref: "step_1",
122
+ schema_key: messageType.key,
123
+ schema_variant_key: variant.key,
124
+ values
125
+ }
126
+ ]
127
+ };
128
+ return {
129
+ [_processorisomorphic.GUIDE_JSON]: guideJson
130
+ };
131
+ };
132
+ const generateGuideDir = async (guideDirCtx, attrs)=>{
133
+ const bundle = scaffoldGuideDirBundle(attrs);
134
+ return (0, _writer.writeGuideDirFromBundle)(guideDirCtx, bundle);
135
+ };
136
+ const generateGuideFromTemplate = async (guideDirCtx, templateString, attrs)=>{
137
+ let tempDir;
138
+ try {
139
+ // Download the template directory into a temp directory
140
+ tempDir = await _templates.downloadTemplate(templateString);
141
+ // Create a guide directory context for the temp directory
142
+ const tempGuideDirCtx = {
143
+ type: "guide",
144
+ key: "temp",
145
+ abspath: tempDir,
146
+ exists: true
147
+ };
148
+ // Read the guide.json from the temp directory we downloaded
149
+ const [guide, errors] = await (0, _reader.readGuideDir)(tempGuideDirCtx, {
150
+ withExtractedFiles: true
151
+ });
152
+ if (errors.length > 0 || !guide) {
153
+ throw new Error(`Invalid guide template: ${errors.join(", ")}`);
154
+ }
155
+ // Modify the guide data with the new attributes
156
+ guide.name = attrs.name;
157
+ // Finally, we write the guide into the target guide directory
158
+ (0, _writer.writeGuideDirFromData)(guideDirCtx, guide);
159
+ return;
160
+ } finally{
161
+ await _templates.cleanupTempDir(tempDir);
162
+ }
163
+ };
@@ -42,6 +42,7 @@ const _core = require("@oclif/core");
42
42
  const _fsextra = /*#__PURE__*/ _interop_require_wildcard(require("fs-extra"));
43
43
  const _lodash = require("lodash");
44
44
  const _quicktypecore = require("quicktype-core");
45
+ const _projectconfig = require("../../helpers/project-config");
45
46
  const _processorisomorphic = require("./processor.isomorphic");
46
47
  function _getRequireWildcardCache(nodeInterop) {
47
48
  if (typeof WeakMap !== "function") return null;
@@ -107,7 +108,7 @@ const lsGuideJson = async (dirPath)=>{
107
108
  return exists ? guideJsonPath : undefined;
108
109
  };
109
110
  const isGuideDir = async (dirPath)=>Boolean(await lsGuideJson(dirPath));
110
- const ensureValidCommandTarget = async (props, runContext)=>{
111
+ const ensureValidCommandTarget = async (props, runContext, projectConfig)=>{
111
112
  const { args, flags } = props;
112
113
  const { commandId, resourceDir: resourceDirCtx, cwd: runCwd } = runContext;
113
114
  // If the target resource is a different type than the current resource dir
@@ -119,6 +120,8 @@ const ensureValidCommandTarget = async (props, runContext)=>{
119
120
  if (flags.all && args.guideKey) {
120
121
  return _core.ux.error(`guideKey arg \`${args.guideKey}\` cannot also be provided when using --all`);
121
122
  }
123
+ // Default to knock project config first if present, otherwise cwd.
124
+ const guidesIndexDirCtx = await (0, _projectconfig.resolveResourceDir)(projectConfig, "guide", runCwd);
122
125
  // --all flag is given, which means no guide key arg.
123
126
  if (flags.all) {
124
127
  // If --all flag used inside a guide directory, then require a guides
@@ -126,16 +129,9 @@ const ensureValidCommandTarget = async (props, runContext)=>{
126
129
  if (resourceDirCtx && !flags["guides-dir"]) {
127
130
  return _core.ux.error("Missing required flag guides-dir");
128
131
  }
129
- // Targeting all guide dirs in the guides index dir.
130
- // TODO: Default to the knock project config first if present before cwd.
131
- const defaultToCwd = {
132
- abspath: runCwd,
133
- exists: true
134
- };
135
- const indexDirCtx = flags["guides-dir"] || defaultToCwd;
136
132
  return {
137
133
  type: "guidesIndexDir",
138
- context: indexDirCtx
134
+ context: flags["guides-dir"] || guidesIndexDirCtx
139
135
  };
140
136
  }
141
137
  // Guide key arg is given, which means no --all flag.
@@ -143,7 +139,7 @@ const ensureValidCommandTarget = async (props, runContext)=>{
143
139
  if (resourceDirCtx && resourceDirCtx.key !== args.guideKey) {
144
140
  return _core.ux.error(`Cannot run ${commandId} \`${args.guideKey}\` inside another guide directory:\n${resourceDirCtx.key}`);
145
141
  }
146
- const targetDirPath = resourceDirCtx ? resourceDirCtx.abspath : _nodepath.resolve(runCwd, args.guideKey);
142
+ const targetDirPath = resourceDirCtx ? resourceDirCtx.abspath : _nodepath.resolve(guidesIndexDirCtx.abspath, args.guideKey);
147
143
  const guideDirCtx = {
148
144
  type: "guide",
149
145
  key: args.guideKey,
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
+ _export_star(require("./generator"), exports);
5
6
  _export_star(require("./helpers"), exports);
6
7
  _export_star(require("./processor.isomorphic"), exports);
7
8
  _export_star(require("./reader"), exports);