@graphql-codegen/cli 2.8.0-alpha-bd464a586.0 → 2.8.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/esm/codegen.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { DetailedError, normalizeOutputParam, normalizeInstanceOrArray, normalizeConfig, getCachedDocumentNodeFromSchema, isDetailedError, } from '@graphql-codegen/plugin-helpers';
2
2
  import { codegen } from '@graphql-codegen/core';
3
3
  import { AggregateError } from '@graphql-tools/utils';
4
- import { Renderer, ErrorRenderer } from './utils/listr-renderer.js';
5
4
  import { GraphQLError } from 'graphql';
6
5
  import { getPluginByName } from './plugins.js';
7
6
  import { getPresetByName } from './presets.js';
@@ -11,8 +10,7 @@ import fs from 'fs';
11
10
  import path from 'path';
12
11
  import { cpus } from 'os';
13
12
  import { createRequire } from 'module';
14
- import Listr from 'listr';
15
- import { isListrError } from './utils/cli-error.js';
13
+ import { Listr } from 'listr2';
16
14
  /**
17
15
  * Poor mans ESM detection.
18
16
  * Looking at this and you have a better method?
@@ -53,39 +51,12 @@ export async function executeCodegen(input) {
53
51
  const config = context.getConfig();
54
52
  const pluginContext = context.getPluginContext();
55
53
  const result = [];
56
- const commonListrOptions = {
57
- exitOnError: true,
58
- };
59
- let listr;
60
- if (process.env.VERBOSE) {
61
- listr = new Listr({
62
- ...commonListrOptions,
63
- renderer: 'verbose',
64
- nonTTYRenderer: 'verbose',
65
- });
66
- }
67
- else if (process.env.NODE_ENV === 'test') {
68
- listr = new Listr({
69
- ...commonListrOptions,
70
- renderer: 'silent',
71
- nonTTYRenderer: 'silent',
72
- });
73
- }
74
- else {
75
- listr = new Listr({
76
- ...commonListrOptions,
77
- renderer: config.silent ? 'silent' : config.errorsOnly ? ErrorRenderer : Renderer,
78
- nonTTYRenderer: config.silent ? 'silent' : 'default',
79
- collapse: true,
80
- clearOutput: false,
81
- });
82
- }
83
54
  let rootConfig = {};
84
55
  let rootSchemas;
85
56
  let rootDocuments;
86
57
  const generates = {};
87
58
  const cache = createCache();
88
- function wrapTask(task, source, taskName) {
59
+ function wrapTask(task, source, taskName, ctx) {
89
60
  return () => {
90
61
  return context.profiler.run(async () => {
91
62
  try {
@@ -95,6 +66,7 @@ export async function executeCodegen(input) {
95
66
  if (source && !(error instanceof GraphQLError)) {
96
67
  error.source = source;
97
68
  }
69
+ ctx.errors.push(error);
98
70
  throw error;
99
71
  }
100
72
  }, taskName);
@@ -165,123 +137,109 @@ export async function executeCodegen(input) {
165
137
  `);
166
138
  }
167
139
  }
168
- listr.add({
169
- title: 'Parse configuration',
170
- task: () => normalize(),
171
- });
172
- listr.add({
173
- title: 'Generate outputs',
174
- task: () => {
175
- return new Listr(Object.keys(generates).map(filename => {
176
- const outputConfig = generates[filename];
177
- const hasPreset = !!outputConfig.preset;
178
- return {
179
- title: hasPreset
140
+ const isTest = process.env.NODE_ENV === 'test';
141
+ const tasks = new Listr([
142
+ {
143
+ title: 'Parse Configuration',
144
+ task: () => normalize(),
145
+ },
146
+ {
147
+ title: 'Generate outputs',
148
+ task: (ctx, task) => {
149
+ const generateTasks = Object.keys(generates).map(filename => {
150
+ const outputConfig = generates[filename];
151
+ const hasPreset = !!outputConfig.preset;
152
+ const title = hasPreset
180
153
  ? `Generate to ${filename} (using EXPERIMENTAL preset "${outputConfig.preset}")`
181
- : `Generate ${filename}`,
182
- task: () => {
183
- let outputSchemaAst;
184
- let outputSchema;
185
- const outputFileTemplateConfig = outputConfig.config || {};
186
- let outputDocuments = [];
187
- const outputSpecificSchemas = normalizeInstanceOrArray(outputConfig.schema);
188
- const outputSpecificDocuments = normalizeInstanceOrArray(outputConfig.documents);
189
- return new Listr([
190
- {
191
- title: 'Load GraphQL schemas',
192
- task: wrapTask(async () => {
193
- debugLog(`[CLI] Loading Schemas`);
194
- const schemaPointerMap = {};
195
- const allSchemaUnnormalizedPointers = [...rootSchemas, ...outputSpecificSchemas];
196
- for (const unnormalizedPtr of allSchemaUnnormalizedPointers) {
197
- if (typeof unnormalizedPtr === 'string') {
198
- schemaPointerMap[unnormalizedPtr] = {};
199
- }
200
- else if (typeof unnormalizedPtr === 'object') {
201
- Object.assign(schemaPointerMap, unnormalizedPtr);
202
- }
203
- }
204
- const hash = JSON.stringify(schemaPointerMap);
205
- const result = await cache('schema', hash, async () => {
206
- const outputSchemaAst = await context.loadSchema(schemaPointerMap);
207
- const outputSchema = getCachedDocumentNodeFromSchema(outputSchemaAst);
208
- return {
209
- outputSchemaAst,
210
- outputSchema,
211
- };
212
- });
213
- outputSchemaAst = await result.outputSchemaAst;
214
- outputSchema = result.outputSchema;
215
- }, filename, `Load GraphQL schemas: ${filename}`),
216
- },
217
- {
218
- title: 'Load GraphQL documents',
219
- task: wrapTask(async () => {
220
- debugLog(`[CLI] Loading Documents`);
221
- // get different cache for shared docs and output specific docs
222
- const documentPointerMap = {};
223
- const allDocumentsUnnormalizedPointers = [...rootDocuments, ...outputSpecificDocuments];
224
- for (const unnormalizedPtr of allDocumentsUnnormalizedPointers) {
225
- if (typeof unnormalizedPtr === 'string') {
226
- documentPointerMap[unnormalizedPtr] = {};
154
+ : `Generate ${filename}`;
155
+ return {
156
+ title,
157
+ task: (_, subTask) => {
158
+ let outputSchemaAst;
159
+ let outputSchema;
160
+ const outputFileTemplateConfig = outputConfig.config || {};
161
+ let outputDocuments = [];
162
+ const outputSpecificSchemas = normalizeInstanceOrArray(outputConfig.schema);
163
+ const outputSpecificDocuments = normalizeInstanceOrArray(outputConfig.documents);
164
+ return subTask.newListr([
165
+ {
166
+ title: 'Load GraphQL schemas',
167
+ task: wrapTask(async () => {
168
+ debugLog(`[CLI] Loading Schemas`);
169
+ const schemaPointerMap = {};
170
+ const allSchemaDenormalizedPointers = [...rootSchemas, ...outputSpecificSchemas];
171
+ for (const denormalizedPtr of allSchemaDenormalizedPointers) {
172
+ if (typeof denormalizedPtr === 'string') {
173
+ schemaPointerMap[denormalizedPtr] = {};
174
+ }
175
+ else if (typeof denormalizedPtr === 'object') {
176
+ Object.assign(schemaPointerMap, denormalizedPtr);
177
+ }
227
178
  }
228
- else if (typeof unnormalizedPtr === 'object') {
229
- Object.assign(documentPointerMap, unnormalizedPtr);
179
+ const hash = JSON.stringify(schemaPointerMap);
180
+ const result = await cache('schema', hash, async () => {
181
+ const outputSchemaAst = await context.loadSchema(schemaPointerMap);
182
+ const outputSchema = getCachedDocumentNodeFromSchema(outputSchemaAst);
183
+ return {
184
+ outputSchemaAst,
185
+ outputSchema,
186
+ };
187
+ });
188
+ outputSchemaAst = result.outputSchemaAst;
189
+ outputSchema = result.outputSchema;
190
+ }, filename, `Load GraphQL schemas: ${filename}`, ctx),
191
+ },
192
+ {
193
+ title: 'Load GraphQL documents',
194
+ task: wrapTask(async () => {
195
+ debugLog(`[CLI] Loading Documents`);
196
+ const documentPointerMap = {};
197
+ const allDocumentsDenormalizedPointers = [...rootDocuments, ...outputSpecificDocuments];
198
+ for (const denormalizedPtr of allDocumentsDenormalizedPointers) {
199
+ if (typeof denormalizedPtr === 'string') {
200
+ documentPointerMap[denormalizedPtr] = {};
201
+ }
202
+ else if (typeof denormalizedPtr === 'object') {
203
+ Object.assign(documentPointerMap, denormalizedPtr);
204
+ }
230
205
  }
231
- }
232
- const hash = JSON.stringify(documentPointerMap);
233
- const result = await cache('documents', hash, async () => {
234
- const documents = await context.loadDocuments(documentPointerMap);
235
- return {
236
- documents,
206
+ const hash = JSON.stringify(documentPointerMap);
207
+ const result = await cache('documents', hash, async () => {
208
+ const documents = await context.loadDocuments(documentPointerMap);
209
+ return {
210
+ documents,
211
+ };
212
+ });
213
+ outputDocuments = result.documents;
214
+ }, filename, `Load GraphQL documents: ${filename}`, ctx),
215
+ },
216
+ {
217
+ title: 'Generate',
218
+ task: wrapTask(async () => {
219
+ debugLog(`[CLI] Generating output`);
220
+ const normalizedPluginsArray = normalizeConfig(outputConfig.plugins);
221
+ const pluginLoader = config.pluginLoader || makeDefaultLoader(context.cwd);
222
+ const pluginPackages = await Promise.all(normalizedPluginsArray.map(plugin => getPluginByName(Object.keys(plugin)[0], pluginLoader)));
223
+ const preset = hasPreset
224
+ ? typeof outputConfig.preset === 'string'
225
+ ? await getPresetByName(outputConfig.preset, makeDefaultLoader(context.cwd))
226
+ : outputConfig.preset
227
+ : null;
228
+ const pluginMap = Object.fromEntries(pluginPackages.map((pkg, i) => {
229
+ const plugin = normalizedPluginsArray[i];
230
+ const name = Object.keys(plugin)[0];
231
+ return [name, pkg];
232
+ }));
233
+ const mergedConfig = {
234
+ ...rootConfig,
235
+ ...(typeof outputFileTemplateConfig === 'string'
236
+ ? { value: outputFileTemplateConfig }
237
+ : outputFileTemplateConfig),
237
238
  };
238
- });
239
- outputDocuments = await result.documents;
240
- }, filename, `Load GraphQL documents: ${filename}`),
241
- },
242
- {
243
- title: 'Generate',
244
- task: wrapTask(async () => {
245
- debugLog(`[CLI] Generating output`);
246
- const normalizedPluginsArray = normalizeConfig(outputConfig.plugins);
247
- const pluginLoader = config.pluginLoader || makeDefaultLoader(context.cwd);
248
- const pluginPackages = await Promise.all(normalizedPluginsArray.map(plugin => getPluginByName(Object.keys(plugin)[0], pluginLoader)));
249
- const pluginMap = {};
250
- const preset = hasPreset
251
- ? typeof outputConfig.preset === 'string'
252
- ? await getPresetByName(outputConfig.preset, makeDefaultLoader(context.cwd))
253
- : outputConfig.preset
254
- : null;
255
- pluginPackages.forEach((pluginPackage, i) => {
256
- const plugin = normalizedPluginsArray[i];
257
- const name = Object.keys(plugin)[0];
258
- pluginMap[name] = pluginPackage;
259
- });
260
- const mergedConfig = {
261
- ...rootConfig,
262
- ...(typeof outputFileTemplateConfig === 'string'
263
- ? { value: outputFileTemplateConfig }
264
- : outputFileTemplateConfig),
265
- };
266
- let outputs = [];
267
- if (hasPreset) {
268
- outputs = await context.profiler.run(async () => preset.buildGeneratesSection({
269
- baseOutputDir: filename,
270
- presetConfig: outputConfig.presetConfig || {},
271
- plugins: normalizedPluginsArray,
272
- schema: outputSchema,
273
- schemaAst: outputSchemaAst,
274
- documents: outputDocuments,
275
- config: mergedConfig,
276
- pluginMap,
277
- pluginContext,
278
- profiler: context.profiler,
279
- }), `Build Generates Section: ${filename}`);
280
- }
281
- else {
282
- outputs = [
283
- {
284
- filename,
239
+ const outputs = hasPreset
240
+ ? await context.profiler.run(async () => preset.buildGeneratesSection({
241
+ baseOutputDir: filename,
242
+ presetConfig: outputConfig.presetConfig || {},
285
243
  plugins: normalizedPluginsArray,
286
244
  schema: outputSchema,
287
245
  schemaAst: outputSchemaAst,
@@ -290,50 +248,67 @@ export async function executeCodegen(input) {
290
248
  pluginMap,
291
249
  pluginContext,
292
250
  profiler: context.profiler,
293
- },
294
- ];
295
- }
296
- const process = async (outputArgs) => {
297
- const output = await codegen({
298
- ...outputArgs,
299
- cache,
300
- });
301
- result.push({
302
- filename: outputArgs.filename,
303
- content: output,
304
- hooks: outputConfig.hooks || {},
305
- });
306
- };
307
- await context.profiler.run(() => Promise.all(outputs.map(process)), `Codegen: ${filename}`);
308
- }, filename, `Generate: ${filename}`),
309
- },
310
- ], {
311
- // it stops when one of tasks failed
312
- exitOnError: true,
313
- });
314
- },
315
- };
316
- }), {
317
- // it doesn't stop when one of tasks failed, to finish at least some of outputs
318
- exitOnError: false,
319
- concurrent: cpus().length,
320
- });
251
+ }), `Build Generates Section: ${filename}`)
252
+ : [
253
+ {
254
+ filename,
255
+ plugins: normalizedPluginsArray,
256
+ schema: outputSchema,
257
+ schemaAst: outputSchemaAst,
258
+ documents: outputDocuments,
259
+ config: mergedConfig,
260
+ pluginMap,
261
+ pluginContext,
262
+ profiler: context.profiler,
263
+ },
264
+ ];
265
+ const process = async (outputArgs) => {
266
+ const output = await codegen({
267
+ ...outputArgs,
268
+ cache,
269
+ });
270
+ result.push({
271
+ filename: outputArgs.filename,
272
+ content: output,
273
+ hooks: outputConfig.hooks || {},
274
+ });
275
+ };
276
+ await context.profiler.run(() => Promise.all(outputs.map(process)), `Codegen: ${filename}`);
277
+ }, filename, `Generate: ${filename}`, ctx),
278
+ },
279
+ ], {
280
+ // it stops when of the tasks failed
281
+ exitOnError: true,
282
+ });
283
+ },
284
+ // It doesn't stop when one of tasks failed, to finish at least some of outputs
285
+ exitOnError: false,
286
+ concurrent: cpus().length,
287
+ };
288
+ });
289
+ return task.newListr(generateTasks);
290
+ },
321
291
  },
292
+ ], {
293
+ rendererOptions: {
294
+ clearOutput: false,
295
+ collapse: true,
296
+ },
297
+ ctx: { errors: [] },
298
+ rendererSilent: isTest || config.silent,
299
+ exitOnError: true,
322
300
  });
323
- try {
324
- await listr.run();
325
- }
326
- catch (err) {
327
- if (isListrError(err)) {
328
- const allErrs = err.errors.map(subErr => isDetailedError(subErr)
329
- ? `${subErr.message} for "${subErr.source}"${subErr.details}`
330
- : subErr.message || subErr.toString());
331
- const newErr = new AggregateError(err.errors, `${err.message} ${allErrs.join('\n\n')}`);
332
- // Best-effort to all stack traces for debugging
333
- newErr.stack = `${newErr.stack}\n\n${err.errors.map(subErr => subErr.stack).join('\n\n')}`;
334
- throw newErr;
335
- }
336
- throw err;
301
+ // All the errors throw in `listr2` are collected in context
302
+ // Running tasks doesn't throw anything
303
+ const executedContext = await tasks.run();
304
+ if (executedContext.errors.length > 0) {
305
+ const errors = executedContext.errors.map(subErr => isDetailedError(subErr)
306
+ ? `${subErr.message} for "${subErr.source}"${subErr.details}`
307
+ : subErr.message || subErr.toString());
308
+ const newErr = new AggregateError(executedContext.errors, `${errors.join('\n\n')}`);
309
+ // Best-effort to all stack traces for debugging
310
+ newErr.stack = `${newErr.stack}\n\n${executedContext.errors.map(subErr => subErr.stack).join('\n\n')}`;
311
+ throw newErr;
337
312
  }
338
313
  return result;
339
314
  }
package/esm/config.js CHANGED
@@ -169,7 +169,7 @@ export function updateContextWithCliFlags(context, cliFlags) {
169
169
  const config = {
170
170
  configFilePath: context.filepath,
171
171
  };
172
- if (cliFlags.watch) {
172
+ if (cliFlags.watch !== undefined) {
173
173
  config.watch = cliFlags.watch;
174
174
  }
175
175
  if (cliFlags.overwrite === true) {
@@ -181,6 +181,10 @@ export function updateContextWithCliFlags(context, cliFlags) {
181
181
  if (cliFlags.errorsOnly === true) {
182
182
  config.errorsOnly = cliFlags.errorsOnly;
183
183
  }
184
+ if (cliFlags['ignore-no-documents'] !== undefined) {
185
+ // for some reason parsed value is `'false'` string so this ensure it always is a boolean.
186
+ config.ignoreNoDocuments = cliFlags['ignore-no-documents'] === true;
187
+ }
184
188
  if (cliFlags.project) {
185
189
  context.useProject(cliFlags.project);
186
190
  }
@@ -2,7 +2,7 @@ import chalk from 'chalk';
2
2
  import { resolve, relative } from 'path';
3
3
  import { writeFileSync, readFileSync } from 'fs';
4
4
  import detectIndent from 'detect-indent';
5
- import getLatestVersion from 'latest-version';
5
+ import { getLatestVersion } from '../utils/get-latest-version.js';
6
6
  // Parses config and writes it to a file
7
7
  export async function writeConfig(answers, config) {
8
8
  const YAML = await import('json-to-pretty-yaml').then(m => ('default' in m ? m.default : m));
package/esm/load.js CHANGED
@@ -45,16 +45,16 @@ export async function loadSchema(schemaPointers, config) {
45
45
 
46
46
  ${e.message || e}
47
47
  ${e.stack || ''}
48
-
48
+
49
49
  GraphQL Code Generator supports:
50
50
  - ES Modules and CommonJS exports (export as default or named export "schema")
51
51
  - Introspection JSON File
52
52
  - URL of GraphQL endpoint
53
53
  - Multiple files with type definitions (glob expression)
54
54
  - String in config file
55
-
55
+
56
56
  Try to use one of above options and run codegen again.
57
-
57
+
58
58
  `);
59
59
  }
60
60
  }
@@ -77,12 +77,19 @@ export async function loadDocuments(documentPointers, config) {
77
77
  }
78
78
  ignore.push(join(process.cwd(), generatePath));
79
79
  }
80
- const loadedFromToolkit = await loadDocumentsToolkit(documentPointers, {
81
- ...defaultDocumentsLoadOptions,
82
- ignore,
83
- loaders,
84
- ...config,
85
- ...config.config,
86
- });
87
- return loadedFromToolkit;
80
+ try {
81
+ const loadedFromToolkit = await loadDocumentsToolkit(documentPointers, {
82
+ ...defaultDocumentsLoadOptions,
83
+ ignore,
84
+ loaders,
85
+ ...config,
86
+ ...config.config,
87
+ });
88
+ return loadedFromToolkit;
89
+ }
90
+ catch (error) {
91
+ if (config.ignoreNoDocuments)
92
+ return [];
93
+ throw error;
94
+ }
88
95
  }
@@ -0,0 +1,11 @@
1
+ import { fetch } from 'cross-undici-fetch';
2
+ /**
3
+ * Fetches the version directly from the registry instead of depending on
4
+ * an ESM only module as latest-version does.
5
+ * @param packageName
6
+ */
7
+ export async function getLatestVersion(packageName) {
8
+ return fetch(`https://unpkg.com/${packageName}/package.json`)
9
+ .then(res => res.json())
10
+ .then(pkg => pkg.version);
11
+ }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@graphql-codegen/cli",
3
- "version": "2.8.0-alpha-bd464a586.0",
3
+ "version": "2.8.0",
4
4
  "peerDependencies": {
5
5
  "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
6
6
  },
7
7
  "dependencies": {
8
- "@graphql-codegen/core": "2.6.0-alpha-bd464a586.0",
9
- "@graphql-codegen/plugin-helpers": "^2.5.0-alpha-bd464a586.0",
8
+ "@graphql-codegen/core": "2.6.0",
9
+ "@graphql-codegen/plugin-helpers": "^2.5.0",
10
10
  "@graphql-tools/apollo-engine-loader": "^7.3.1",
11
11
  "@graphql-tools/code-file-loader": "^7.3.0",
12
12
  "@graphql-tools/git-loader": "^7.2.0",
@@ -19,24 +19,20 @@
19
19
  "@graphql-tools/utils": "^8.8.0",
20
20
  "ansi-escapes": "^4.3.1",
21
21
  "chalk": "^4.1.0",
22
- "change-case-all": "1.0.14",
23
22
  "chokidar": "^3.5.2",
24
- "common-tags": "^1.8.0",
25
23
  "cosmiconfig": "^7.0.0",
24
+ "cross-undici-fetch": "^0.4.11",
26
25
  "debounce": "^1.2.0",
27
26
  "detect-indent": "^6.0.0",
28
27
  "graphql-config": "^4.3.1",
29
28
  "inquirer": "^8.0.0",
30
29
  "is-glob": "^4.0.1",
31
30
  "json-to-pretty-yaml": "^1.2.2",
32
- "latest-version": "5.1.0",
33
- "listr": "^0.14.3",
34
- "listr-update-renderer": "^0.5.0",
31
+ "listr2": "^4.0.5",
35
32
  "log-symbols": "^4.0.0",
36
33
  "mkdirp": "^1.0.4",
37
34
  "string-env-interpolation": "^1.0.1",
38
35
  "ts-log": "^2.2.3",
39
- "wrap-ansi": "^7.0.0",
40
36
  "yaml": "^1.10.0",
41
37
  "yargs": "^17.0.0"
42
38
  },
@@ -10,6 +10,7 @@ export declare type YamlCliFlags = {
10
10
  silent: boolean;
11
11
  errorsOnly: boolean;
12
12
  profile: boolean;
13
+ ignoreNoDocuments?: boolean;
13
14
  };
14
15
  export declare function generateSearchPlaces(moduleName: string): string[];
15
16
  export interface LoadCodegenConfigOptions {
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Fetches the version directly from the registry instead of depending on
3
+ * an ESM only module as latest-version does.
4
+ * @param packageName
5
+ */
6
+ export declare function getLatestVersion(packageName: string): Promise<string>;
@@ -1,37 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.indentString = void 0;
4
- /**
5
- Indent each line in a string.
6
- @param string - The string to indent.
7
- @param count - How many times you want `options.indent` repeated. Default: `1`.
8
- @example
9
- ```
10
- import indentString from 'indent-string';
11
- indentString('Unicorns\nRainbows', 4);
12
- //=> ' Unicorns\n Rainbows'
13
- indentString('Unicorns\nRainbows', 4, {indent: '♥'});
14
- //=> '♥♥♥♥Unicorns\n♥♥♥♥Rainbows'
15
- ```
16
- */
17
- function indentString(string, count = 1, options = {}) {
18
- const { indent = ' ', includeEmptyLines = false } = options;
19
- if (typeof string !== 'string') {
20
- throw new TypeError(`Expected \`input\` to be a \`string\`, got \`${typeof string}\``);
21
- }
22
- if (typeof count !== 'number') {
23
- throw new TypeError(`Expected \`count\` to be a \`number\`, got \`${typeof count}\``);
24
- }
25
- if (count < 0) {
26
- throw new RangeError(`Expected \`count\` to be at least 0, got \`${count}\``);
27
- }
28
- if (typeof indent !== 'string') {
29
- throw new TypeError(`Expected \`options.indent\` to be a \`string\`, got \`${typeof indent}\``);
30
- }
31
- if (count === 0) {
32
- return string;
33
- }
34
- const regex = includeEmptyLines ? /^/gm : /^(?!\s*$)/gm;
35
- return string.replace(regex, indent.repeat(count));
36
- }
37
- exports.indentString = indentString;