juisy 2.0.0-beta.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.
Files changed (71) hide show
  1. package/README.md +211 -0
  2. package/bin/cli/cli.js +23 -0
  3. package/bin/cli/cmds/changelog.js +41 -0
  4. package/bin/cli/cmds/docs/generate-api.js +22 -0
  5. package/bin/cli/cmds/docs/generate-cli.js +11 -0
  6. package/bin/cli/cmds/docs/generate-readme.js +11 -0
  7. package/bin/cli/cmds/docs/index.js +22 -0
  8. package/bin/cli/cmds/docs/lint.js +42 -0
  9. package/bin/cli/cmds/eject.js +28 -0
  10. package/bin/cli/cmds/git-hooks/index.js +20 -0
  11. package/bin/cli/cmds/git-hooks/reset.js +48 -0
  12. package/bin/cli/cmds/git-hooks/sync.js +19 -0
  13. package/bin/cli/cmds/index.js +15 -0
  14. package/bin/cli/cmds/print-globals.js +28 -0
  15. package/bin/cli/cmds/release.js +231 -0
  16. package/bin/cli/cmds/squeeze.js +269 -0
  17. package/bin/cli/cmds/test.js +33 -0
  18. package/bin/cli/index.js +9 -0
  19. package/bin/cli/lib/docs/generate-api-doc.js +78 -0
  20. package/bin/cli/lib/version/update-version.js +52 -0
  21. package/bin/scripts/commit-msg.js +32 -0
  22. package/bin/scripts/pre-commit.js +24 -0
  23. package/dist/DataExporter.d.ts +67 -0
  24. package/dist/cli/CLIFactory.d.ts +19 -0
  25. package/dist/cli/Command.d.ts +44 -0
  26. package/dist/cli/InterfaceUtils.d.ts +53 -0
  27. package/dist/cli/OutputUtils.d.ts +123 -0
  28. package/dist/cli/command-visitors/command-handler-injections.d.ts +10 -0
  29. package/dist/cli/command-visitors/get-command-meta.d.ts +10 -0
  30. package/dist/cli/command-visitors/index.d.ts +9 -0
  31. package/dist/cli/command-visitors/private-command.d.ts +16 -0
  32. package/dist/cli/create-engine.d.ts +7 -0
  33. package/dist/cli/extract-usage.d.ts +72 -0
  34. package/dist/cli/index.d.ts +20 -0
  35. package/dist/cli/index.js +559 -0
  36. package/dist/cli/types.d.ts +112 -0
  37. package/dist/cli/utils.d.ts +19 -0
  38. package/dist/eject.d.ts +22 -0
  39. package/dist/get-package-info.d.ts +6 -0
  40. package/dist/index.d.ts +11 -0
  41. package/dist/index.js +244 -0
  42. package/dist/project-globals.d.ts +63 -0
  43. package/dist/templater/Templater.d.ts +23 -0
  44. package/dist/templater/index.d.ts +6 -0
  45. package/dist/templater/index.js +330 -0
  46. package/dist/templater/markdown-templater/ReadmeTemplater.d.ts +154 -0
  47. package/dist/templater/markdown-templater/index.d.ts +25 -0
  48. package/dist/templater/types.d.ts +10 -0
  49. package/dist/utils/misc.d.ts +21 -0
  50. package/package.json +179 -0
  51. package/src/index.js +507 -0
  52. package/template/CHANGELOG.md +0 -0
  53. package/template/bin/cli/cli.js +27 -0
  54. package/template/bin/cli/cmds/changelog.js +71 -0
  55. package/template/bin/cli/cmds/docs.js +30 -0
  56. package/template/bin/cli/cmds/docs_cmds/generate-api.js +75 -0
  57. package/template/bin/cli/cmds/docs_cmds/generate-readme.js +51 -0
  58. package/template/bin/cli/cmds/git-hooks.js +30 -0
  59. package/template/bin/cli/cmds/git_hooks_cmds/reset.js +76 -0
  60. package/template/bin/cli/cmds/git_hooks_cmds/sync.js +44 -0
  61. package/template/bin/cli/cmds/release.js +219 -0
  62. package/template/bin/cli/index.js +7 -0
  63. package/template/bin/cli/lib/docs/generate-api-doc.js +33 -0
  64. package/template/bin/cli/lib/release/generate-release-note.js +3 -0
  65. package/template/bin/cli/lib/version/update-version.js +51 -0
  66. package/template/bin/scripts/commit-msg.js +42 -0
  67. package/template/bin/scripts/pre-commit.js +32 -0
  68. package/template/docs/api/docs.config.js +10 -0
  69. package/template/docs/readme/config.js +22 -0
  70. package/template/docs/readme/readme.js +70 -0
  71. package/template/docs/readme/template.md +53 -0
@@ -0,0 +1,22 @@
1
+ import { LogLevelDesc } from 'loglevel';
2
+ type EjectOptions = {
3
+ force?: boolean;
4
+ targetDir?: string;
5
+ logLevel?: LogLevelDesc;
6
+ processor?: (content: string, identifier: string) => string | Promise<string>;
7
+ onError?: (identifier: string, fromPath: string, toPath: string, err: Error) => void | Promise<void>;
8
+ onSuccess?: (identifier: string, fromPath: string, toPath: string) => void | Promise<void>;
9
+ };
10
+ /**
11
+ * Eject from file from template to user project
12
+ * @param identifier - The identifier of the file to eject (relative to juisy/template folder). Can be a glob pattern (ex: `bin\/**\/*`).
13
+ * @param options - The options object
14
+ * @param options.force - If true, the target files will be overwritten
15
+ * @param options.targetDir - The target directory absolute path
16
+ * @param options.processor - The template file content processor function.
17
+ * Takes content as unique argument and must return string or a Promise that resolves a string
18
+ * @param options.onError - The callback if an error occurs
19
+ * @param options.onSuccess - The callback in of success
20
+ */
21
+ export default function eject(identifier: string, options?: EjectOptions): Promise<void>;
22
+ export {};
@@ -0,0 +1,6 @@
1
+ import { IPackageJson as PackageJson } from 'package-json-type';
2
+ /**
3
+ * Get package.json content
4
+ * @returns The package.json content as object
5
+ */
6
+ export default function getPackageInfo(): PackageJson;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Public API of juisy library
3
+ * @module juisy
4
+ * @author Hervé Perchec
5
+ */
6
+ import * as utils from './utils/misc';
7
+ export { default as eject } from './eject';
8
+ export * from './DataExporter';
9
+ export { default as getPackageInfo } from './get-package-info';
10
+ export * from './project-globals';
11
+ export { utils };
package/dist/index.js ADDED
@@ -0,0 +1,244 @@
1
+ /*!
2
+ * juisy v1.4.0
3
+ * Copyright © 2022-Present Hervé Perchec
4
+ */
5
+
6
+ import path, { dirname } from 'node:path';
7
+ import { readdir, readFileSync } from 'node:fs';
8
+ import { glob } from 'glob';
9
+ import fs from 'fs-extra';
10
+ import { fileURLToPath, pathToFileURL } from 'node:url';
11
+ import { packageDirectorySync } from 'pkg-dir';
12
+ import log from 'loglevel';
13
+ import { Templater } from 'juisy/templater';
14
+ import yaml from 'yaml';
15
+ import { json2csv } from 'json-2-csv';
16
+ import jstoxml from 'jstoxml';
17
+ import dotenv from '@dotenvx/dotenvx';
18
+ import deepmerge from 'deepmerge';
19
+
20
+ function getFileUrls(pattern, options) {
21
+ const files = glob.sync(pattern, options || {});
22
+ return files;
23
+ }
24
+ async function* getFiles(dir) {
25
+ const dirents = await readdir(dir, { withFileTypes: true });
26
+ for (const dirent of dirents) {
27
+ const res = path.resolve(dir, dirent.name);
28
+ if (dirent.isDirectory()) {
29
+ yield* getFiles(res);
30
+ } else {
31
+ yield res;
32
+ }
33
+ }
34
+ }
35
+
36
+ const misc = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
37
+ __proto__: null,
38
+ getFileUrls,
39
+ getFiles
40
+ }, Symbol.toStringTag, { value: 'Module' }));
41
+
42
+ const __filename = fileURLToPath(import.meta.url);
43
+ const __dirname = dirname(__filename);
44
+ const templater = new Templater("ejs", {
45
+ engineOptions: {
46
+ // ...
47
+ }
48
+ });
49
+ async function isDirectory(filePath) {
50
+ try {
51
+ const stats = await fs.stat(filePath);
52
+ return stats.isDirectory();
53
+ } catch (err) {
54
+ return false;
55
+ }
56
+ }
57
+ async function isFile(filePath) {
58
+ try {
59
+ const stats = await fs.stat(filePath);
60
+ return stats.isFile();
61
+ } catch (err) {
62
+ return false;
63
+ }
64
+ }
65
+ async function ejectFile(from, to, identifier = null, options) {
66
+ identifier = identifier || path.basename(from);
67
+ const force = options?.force || false;
68
+ const processor = options?.processor || (async (str, _identifier) => str);
69
+ const onError = options?.onError || (async (_identifier, fromPath, toPath, err) => {
70
+ throw err;
71
+ });
72
+ const onSuccess = options?.onSuccess || (async (_identifier, fromPath, toPath) => {
73
+ log.info(`File ${_identifier} successfuly ejected!`);
74
+ });
75
+ try {
76
+ const write = await fs.exists(to) ? force : true;
77
+ if (write) {
78
+ const template = await fs.readFile(from, { encoding: "utf8" });
79
+ const content = await templater.render(template);
80
+ const processedContent = await processor(content, identifier);
81
+ await fs.ensureFile(to);
82
+ await fs.writeFile(to, processedContent);
83
+ await onSuccess(identifier, from, to);
84
+ } else {
85
+ log.warn(`Target "${to}" already exists. Skipped... Use "force" option to overwrite`);
86
+ }
87
+ } catch (err) {
88
+ await onError(identifier, from, to, err);
89
+ }
90
+ }
91
+ async function eject(identifier, options) {
92
+ const targetDir = options?.targetDir || packageDirectorySync() || process.cwd();
93
+ const logLevel = options?.logLevel || "trace";
94
+ const originalLogLevel = log.getLevel();
95
+ log.setLevel(logLevel);
96
+ if (!identifier) {
97
+ throw new Error("eject: identifier is required");
98
+ } else {
99
+ try {
100
+ const localTemplatePath = path.resolve(__dirname, "../template");
101
+ const fullPath = path.resolve(localTemplatePath, identifier);
102
+ if (await isFile(fullPath)) {
103
+ await ejectFile(fullPath, path.resolve(targetDir, identifier), identifier, {
104
+ processor: options?.processor,
105
+ onSuccess: options?.onSuccess,
106
+ onError: options?.onError
107
+ });
108
+ } else {
109
+ const globPattern = await isDirectory(fullPath) ? identifier + "/**/*" : identifier;
110
+ for (const fileRelativePath of getFileUrls(globPattern, { cwd: localTemplatePath, nodir: true, posix: true })) {
111
+ const fullTemplateFilePath = path.resolve(localTemplatePath, fileRelativePath);
112
+ await ejectFile(fullTemplateFilePath, path.resolve(targetDir, fileRelativePath), fileRelativePath, {
113
+ processor: options?.processor,
114
+ onSuccess: options?.onSuccess,
115
+ onError: options?.onError
116
+ });
117
+ }
118
+ }
119
+ log.info(`Successfully ejected "${identifier}"`);
120
+ } finally {
121
+ log.setLevel(originalLogLevel);
122
+ }
123
+ }
124
+ }
125
+
126
+ const xml = jstoxml.toXML;
127
+ const SUPPORTED_FORMATS = [
128
+ "json",
129
+ "yaml",
130
+ "csv",
131
+ "xml"
132
+ // 'bson',
133
+ // 'msgpack',
134
+ // 'protobuf',
135
+ // 'avro'
136
+ ];
137
+ const jsonExporter = function(data, options = {}) {
138
+ return JSON.stringify(data, options.replacer, options.space);
139
+ };
140
+ const yamlExporter = function(data, options = {}) {
141
+ return yaml.stringify(data, options.replacer, options.exporter);
142
+ };
143
+ const csvExporter = function(data, options) {
144
+ return json2csv(data, options);
145
+ };
146
+ const xmlExporter = function(data, options) {
147
+ return xml(data, options);
148
+ };
149
+ class DataExporter {
150
+ /**
151
+ * The target format
152
+ */
153
+ format;
154
+ /**
155
+ * Creates new instance of Costume
156
+ * @param options - Costume options
157
+ */
158
+ constructor(format) {
159
+ if (SUPPORTED_FORMATS.includes(format)) {
160
+ this.format = format;
161
+ } else {
162
+ throw new Error(`DataConverter: Unsupported "${format}" format`);
163
+ }
164
+ }
165
+ exportSync(data, options) {
166
+ switch (this.format) {
167
+ case "json":
168
+ return jsonExporter(data, options);
169
+ case "yaml":
170
+ return yamlExporter(data, options);
171
+ case "csv":
172
+ return csvExporter(data, options);
173
+ case "xml":
174
+ return xmlExporter(data, options);
175
+ }
176
+ }
177
+ export(...args) {
178
+ return new Promise((resolve, reject) => {
179
+ resolve(this.exportSync(...args));
180
+ });
181
+ }
182
+ }
183
+
184
+ function getPackageInfo() {
185
+ const pkgPath = path.resolve(packageDirectorySync(), "./package.json");
186
+ return JSON.parse(readFileSync(pkgPath, "utf8"));
187
+ }
188
+
189
+ function defineGlobals(config, builder) {
190
+ const env = {
191
+ ...process.env
192
+ };
193
+ let pkg = {};
194
+ try {
195
+ pkg = getPackageInfo();
196
+ } catch (error) {
197
+ console.warn("Unable to find or parse package.json:", error.message);
198
+ }
199
+ const GLOBALS = {
200
+ ENV: {},
201
+ PACKAGE: {
202
+ NAME: pkg.name,
203
+ URL: `https://www.npmjs.com/package/${pkg.name}`
204
+ },
205
+ VERSION: pkg.version,
206
+ AUTHOR: {
207
+ EMAIL: pkg.author?.email,
208
+ NAME: pkg.author?.name,
209
+ URL: pkg.author?.url
210
+ },
211
+ REPOSITORY: {
212
+ TYPE: pkg.repository?.type,
213
+ URL: pkg.repository?.url
214
+ },
215
+ ISSUES_URL: pkg.bugs?.url,
216
+ HOMEPAGE: pkg.homepage
217
+ };
218
+ if (config.env?.map?.length) {
219
+ dotenv.config({
220
+ // Default to not verbose, can be overridden
221
+ debug: false,
222
+ verbose: false,
223
+ quiet: true,
224
+ // Spread user options
225
+ ...config.env.dotenvOptions || {},
226
+ // DON'T write to process.env.
227
+ // We pass custom local temporary object
228
+ processEnv: env
229
+ });
230
+ for (const envVar of config.env.map) {
231
+ GLOBALS.ENV[envVar] = env[envVar];
232
+ }
233
+ }
234
+ const ctx = {
235
+ env,
236
+ pkg
237
+ };
238
+ return deepmerge(GLOBALS, JSON.parse(JSON.stringify(builder(ctx))));
239
+ }
240
+ async function getProjectGlobals(filePath = "./globals.config.js") {
241
+ return (await import(pathToFileURL(path.resolve(filePath)))).default;
242
+ }
243
+
244
+ export { DataExporter, defineGlobals, eject, getPackageInfo, getProjectGlobals, misc as utils };
@@ -0,0 +1,63 @@
1
+ import { DotenvConfigOptions } from '@dotenvx/dotenvx';
2
+ import { IPackageJson as PackageJson, IAuthor as PackageJsonAuthor, IRepository as PackageJsonRepository, IBugs as PackageJsonBugs } from 'package-json-type';
3
+ export type ProjectGlobals = Record<string, string | Record<string, unknown> | undefined> & {
4
+ ENV: NodeJS.ProcessEnv;
5
+ PACKAGE: {
6
+ NAME: PackageJson['name'];
7
+ URL: PackageJson['url'];
8
+ };
9
+ VERSION: PackageJson['version'];
10
+ AUTHOR: {
11
+ EMAIL: PackageJsonAuthor['email'];
12
+ NAME: PackageJsonAuthor['name'];
13
+ URL: PackageJsonAuthor['url'];
14
+ };
15
+ REPOSITORY: {
16
+ TYPE: PackageJsonRepository['type'];
17
+ URL: PackageJsonRepository['url'];
18
+ };
19
+ ISSUES_URL: PackageJsonBugs['url'];
20
+ HOMEPAGE: PackageJson['homepage'];
21
+ };
22
+ export type DotenvOptions = Omit<DotenvConfigOptions, 'processEnv'>;
23
+ export type DefineGlobalsBuilder = (ctx: {
24
+ env: NodeJS.ProcessEnv;
25
+ pkg: PackageJson;
26
+ }) => ProjectGlobals;
27
+ /**
28
+ * Define project globals
29
+ * @param config - The config object
30
+ * @param builder - The builder function
31
+ * @returns The computed globals object
32
+ */
33
+ export declare function defineGlobals(config: {
34
+ env?: {
35
+ map?: string[];
36
+ dotenvOptions?: DotenvOptions;
37
+ };
38
+ }, builder: DefineGlobalsBuilder): {
39
+ [x: string]: /*elided*/ any;
40
+ ENV: NodeJS.ProcessEnv;
41
+ PACKAGE: {
42
+ NAME: PackageJson["name"];
43
+ URL: PackageJson["url"];
44
+ };
45
+ VERSION: string;
46
+ AUTHOR: {
47
+ EMAIL: PackageJsonAuthor["email"];
48
+ NAME: PackageJsonAuthor["name"];
49
+ URL: PackageJsonAuthor["url"];
50
+ };
51
+ REPOSITORY: {
52
+ TYPE: PackageJsonRepository["type"];
53
+ URL: PackageJsonRepository["url"];
54
+ };
55
+ ISSUES_URL: string;
56
+ HOMEPAGE: string;
57
+ };
58
+ /**
59
+ * Get project globals
60
+ * @param filePath - The filePath. Default is `'./globals.config.js'`
61
+ * @returns The resolved project globals object
62
+ */
63
+ export declare function getProjectGlobals(filePath?: string): Promise<ProjectGlobals>;
@@ -0,0 +1,23 @@
1
+ import { SupportedEngines, TemplaterOptions } from './types';
2
+ export declare class Templater {
3
+ constructor(engine: 'handlebars', options?: TemplaterOptions<'handlebars'>);
4
+ constructor(engine: 'ejs', options?: TemplaterOptions<'ejs'>);
5
+ constructor(engine: 'pug', options?: TemplaterOptions<'pug'>);
6
+ constructor(engine: SupportedEngines[number], options?: TemplaterOptions<SupportedEngines[number]>);
7
+ private engine;
8
+ private defaultData;
9
+ private methodsMap;
10
+ /**
11
+ * @returns Get the engine
12
+ */
13
+ getEngine(): unknown;
14
+ /**
15
+ * Renders a template with the given context.
16
+ */
17
+ render(template: string, data?: undefined, options?: any): Promise<string>;
18
+ /**
19
+ * Renders a template file with the given context.
20
+ * @todo not yet implemented
21
+ */
22
+ renderFile(filePath: string, context?: Record<string, any>): string;
23
+ }
@@ -0,0 +1,6 @@
1
+ export { Templater } from './Templater';
2
+ export { MarkdownTemplater } from './markdown-templater';
3
+ export { ReadmeTemplater } from './markdown-templater/ReadmeTemplater';
4
+ export type * from './types';
5
+ export type { MarkdownTemplaterOptions } from './markdown-templater';
6
+ export type { UserConfiguration as ReadmeTemplaterUserConfiguration } from './markdown-templater/ReadmeTemplater';