@firenet-designs/fnd-cli 1.0.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.
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ fnd
2
+ =================
3
+
4
+ A new CLI generated with oclif
5
+
6
+
7
+ [![oclif](https://img.shields.io/badge/cli-oclif-brightgreen.svg)](https://oclif.io)
8
+ [![Version](https://img.shields.io/npm/v/fnd.svg)](https://npmjs.org/package/fnd)
9
+ [![Downloads/week](https://img.shields.io/npm/dw/fnd.svg)](https://npmjs.org/package/fnd)
10
+
11
+
12
+ <!-- toc -->
13
+ * [Usage](#usage)
14
+ * [Commands](#commands)
15
+ <!-- tocstop -->
16
+ # Usage
17
+ <!-- usage -->
18
+ ```sh-session
19
+ $ npm install -g @firenet-designs/fnd-cli
20
+ $ fnd-cli COMMAND
21
+ running command...
22
+ $ fnd-cli (--version)
23
+ @firenet-designs/fnd-cli/1.0.1 darwin-x64 node-v24.12.0
24
+ $ fnd-cli --help [COMMAND]
25
+ USAGE
26
+ $ fnd-cli COMMAND
27
+ ...
28
+ ```
29
+ <!-- usagestop -->
30
+ # Commands
31
+ <!-- commands -->
32
+ * [`fnd-cli create-app`](#fnd-cli-create-app)
33
+ * [`fnd-cli help [COMMAND]`](#fnd-cli-help-command)
34
+
35
+ ## `fnd-cli create-app`
36
+
37
+ sets up a preconfigued vite app directory on for a shopify theme
38
+
39
+ ```
40
+ USAGE
41
+ $ fnd-cli create-app [-d <value>] [-s]
42
+
43
+ FLAGS
44
+ -d, --dir=<value> [default: src] directory to create the vite app in
45
+ -s, --skip-check skips the checking of the parent directory for a shopify theme
46
+
47
+ DESCRIPTION
48
+ sets up a preconfigued vite app directory on for a shopify theme
49
+
50
+ EXAMPLES
51
+ $ fnd-cli create-app
52
+ ```
53
+
54
+ _See code: [src/commands/create-app.ts](https://github.com/FireNet-Designs/fnd-cli/blob/v1.0.1/src/commands/create-app.ts)_
55
+
56
+ ## `fnd-cli help [COMMAND]`
57
+
58
+ Display help for fnd-cli.
59
+
60
+ ```
61
+ USAGE
62
+ $ fnd-cli help [COMMAND...] [-n]
63
+
64
+ ARGUMENTS
65
+ [COMMAND...] Command to show help for.
66
+
67
+ FLAGS
68
+ -n, --nested-commands Include all nested commands in the output.
69
+
70
+ DESCRIPTION
71
+ Display help for fnd-cli.
72
+ ```
73
+
74
+ _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.2.37/src/commands/help.ts)_
75
+ <!-- commandsstop -->
package/bin/dev.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node --loader ts-node/esm --no-warnings=ExperimentalWarning "%~dp0\dev" %*
package/bin/dev.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env -S node --loader ts-node/esm --disable-warning=ExperimentalWarning
2
+
3
+ import {execute} from '@oclif/core'
4
+
5
+ await execute({development: true, dir: import.meta.url})
package/bin/run.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node "%~dp0\run" %*
package/bin/run.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {execute} from '@oclif/core'
4
+
5
+ await execute({dir: import.meta.url})
@@ -0,0 +1,10 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class CreateApp extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ dir: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'skip-check': import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ };
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,154 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { spawn } from 'node:child_process';
4
+ import { mkdirSync, rmSync, statSync } from 'node:fs';
5
+ import { join, resolve } from 'node:path';
6
+ import ora from 'ora';
7
+ import { simpleGit } from 'simple-git';
8
+ /**
9
+ * Executes a series of shell commands sequentially in a single shell session.
10
+ *
11
+ * This function takes an array of shell commands and executes them in sequence
12
+ * within a single shell session, sourcing the user's zsh configuration first.
13
+ * It provides callbacks for handling stdout and stderr data as it's received,
14
+ * and returns the final output, error, and exit code.
15
+ *
16
+ * @param commands - An array of shell commands to execute
17
+ * @param options - Execution options including environment variables and callbacks
18
+ * @returns A promise that resolves with the command output, error, and exit code
19
+ */
20
+ const execCommands = async (commands, options) => {
21
+ const cmdStr = ['source ~/.zshrc', ...commands].join(' && ');
22
+ return new Promise((resolve) => {
23
+ let stdout = '';
24
+ let stderr = '';
25
+ let output = '';
26
+ const child = spawn(cmdStr, {
27
+ ...options,
28
+ shell: 'zsh',
29
+ });
30
+ child.stdout.on('data', (data) => {
31
+ stdout += data;
32
+ output += data;
33
+ options.onOutput?.(output);
34
+ options.onStdout?.(stdout);
35
+ });
36
+ child.stderr.on('data', (data) => {
37
+ stderr += data;
38
+ output += data;
39
+ options.onOutput?.(output);
40
+ options.onStderr?.(stderr);
41
+ });
42
+ child.on('close', (code) => {
43
+ resolve({ code, output, stderr, stdout });
44
+ });
45
+ });
46
+ };
47
+ /**
48
+ * Checks if a directory exists at the specified path.
49
+ *
50
+ * This function attempts to retrieve the file status of the given directory path.
51
+ * If the operation succeeds, it means the directory exists; otherwise, it throws
52
+ * an error indicating that the directory does not exist.
53
+ *
54
+ * @param dirPath - The path to the directory to check
55
+ * @returns true if the directory exists, false otherwise
56
+ */
57
+ const checkDirectoryExists = (dirPath) => {
58
+ try {
59
+ statSync(dirPath);
60
+ return true;
61
+ }
62
+ catch {
63
+ return false;
64
+ }
65
+ };
66
+ class OutputFormatter {
67
+ tail;
68
+ formatting;
69
+ onData = (data) => {
70
+ const lines = data.split('\n').slice(-this.tail);
71
+ const str = lines.join('\n');
72
+ this.ora.suffixText = this.formatting
73
+ ? this.formatting(`\n${str}`.trimEnd())
74
+ : `\n${str}`.trimEnd();
75
+ };
76
+ setOra = (ora) => {
77
+ this.ora = ora;
78
+ };
79
+ ora = null;
80
+ constructor(tail = 10, formatting) {
81
+ this.tail = tail;
82
+ this.formatting = formatting;
83
+ }
84
+ }
85
+ export default class CreateApp extends Command {
86
+ static description = 'sets up a preconfigued vite app directory on for a shopify theme';
87
+ static examples = [
88
+ '<%= config.bin %> <%= command.id %>',
89
+ ];
90
+ static flags = {
91
+ dir: Flags.string({ char: 'd', default: 'src', description: 'directory to create the vite app in' }),
92
+ 'skip-check': Flags.boolean({ char: 's', description: 'skips the checking of the parent directory for a shopify theme' }),
93
+ };
94
+ async run() {
95
+ const { flags } = await this.parse(CreateApp);
96
+ // Checking if a src folder already exists
97
+ const targetDir = join(process.cwd(), flags.dir);
98
+ if (checkDirectoryExists(targetDir)) {
99
+ this.log(chalk.red('Directory', chalk.bold(targetDir), 'already exists'));
100
+ this.log(chalk.yellow('Use the', chalk.bold.white('--dir <dir>'), 'flag to specify a different directory.'));
101
+ return;
102
+ }
103
+ // Checking if the dir is a shopify theme
104
+ if (!flags['skip-check']) {
105
+ const requiredDirs = ['snippets', 'assets', 'sections', 'templates', 'config', 'layout'];
106
+ const targetDirParent = resolve(targetDir, '..');
107
+ const hasAll = requiredDirs.every(dir => checkDirectoryExists(join(targetDirParent, dir)));
108
+ if (!hasAll) {
109
+ this.log(chalk.red('Parent directory of target is not a Shopify theme directory.'));
110
+ this.log(chalk.yellow('Use the', chalk.bold.white('--skip-check'), 'flag to disable checking for a Shopify theme.'));
111
+ return;
112
+ }
113
+ }
114
+ // Making directory
115
+ mkdirSync(targetDir);
116
+ // Cloning the vite template from FireNet-Designs
117
+ const git = simpleGit(targetDir, { binary: 'git' });
118
+ const formatter = new OutputFormatter(10, chalk.dim);
119
+ {
120
+ const spinner = ora('Cloning template...').start();
121
+ await git.clone('git@github.com:FireNet-Designs/vite-template.git', targetDir);
122
+ spinner.succeed('Cloned template');
123
+ }
124
+ {
125
+ const spinner = ora('Deleting .git directory...').start();
126
+ const gitDir = join(targetDir, '.git');
127
+ rmSync(gitDir, { recursive: true });
128
+ spinner.succeed('Deleted .git directory');
129
+ }
130
+ {
131
+ const spinner = ora('Switching/installing node version...').start();
132
+ formatter.setOra(spinner);
133
+ const { stderr } = await execCommands([`cd ${targetDir}`, 'nvm install'], { onOutput: formatter.onData, timeout: 10_000 });
134
+ if (stderr.length > 0 && !stderr.includes('already installed')) {
135
+ spinner.fail('Failed to install node version');
136
+ return this.error(stderr, { code: '1' });
137
+ }
138
+ spinner.suffixText = '';
139
+ spinner.succeed('Installed node version');
140
+ }
141
+ {
142
+ const spinner = ora('Installing dependencies...').start();
143
+ formatter.setOra(spinner);
144
+ const { stderr } = await execCommands([`cd ${targetDir}`, 'nvm use', 'npm ci'], { onOutput: formatter.onData, timeout: 60_000 });
145
+ if (stderr.length > 0) {
146
+ spinner.fail('Failed to install dependencies');
147
+ return this.error(stderr, { code: '1' });
148
+ }
149
+ spinner.suffixText = '';
150
+ spinner.succeed('Installed dependencies');
151
+ }
152
+ this.log("You're good to go :-)");
153
+ }
154
+ }
@@ -0,0 +1,3 @@
1
+ import { Hook } from '@oclif/core';
2
+ declare const hook: Hook<'init'>;
3
+ export default hook;
@@ -0,0 +1,28 @@
1
+ import { simpleGit } from 'simple-git';
2
+ import os from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { rmSync, readFileSync } from 'node:fs';
5
+ import chalk from 'chalk';
6
+ const hook = async function (opts) {
7
+ const tempRepo = join(os.tmpdir(), 'fnd-cli');
8
+ try {
9
+ const git = simpleGit({ binary: 'git' });
10
+ await git.clone('git@github.com:FireNet-Designs/fnd-cli.git', tempRepo, { '--depth': 1 });
11
+ // Getting package.json file to check version
12
+ const pjson = JSON.parse(readFileSync(join(tempRepo, 'package.json'), 'utf-8'));
13
+ const newestVersion = pjson.version;
14
+ const ourVersion = opts.config.pjson.version;
15
+ if (newestVersion !== ourVersion) {
16
+ console.log(chalk.yellow('💡 Version', chalk.green(newestVersion), 'available! Run', chalk.green('`npm i -g git@github.com:FireNet-Designs/fnd-cli.git`'), 'to update to the latest version!\n'));
17
+ }
18
+ }
19
+ finally {
20
+ // Deleting the temp repo but ignoring errors
21
+ try {
22
+ rmSync(tempRepo, { recursive: true });
23
+ }
24
+ catch { }
25
+ ;
26
+ }
27
+ };
28
+ export default hook;
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
@@ -0,0 +1,45 @@
1
+ {
2
+ "commands": {
3
+ "create-app": {
4
+ "aliases": [],
5
+ "args": {},
6
+ "description": "sets up a preconfigued vite app directory on for a shopify theme",
7
+ "examples": [
8
+ "<%= config.bin %> <%= command.id %>"
9
+ ],
10
+ "flags": {
11
+ "dir": {
12
+ "char": "d",
13
+ "description": "directory to create the vite app in",
14
+ "name": "dir",
15
+ "default": "src",
16
+ "hasDynamicHelp": false,
17
+ "multiple": false,
18
+ "type": "option"
19
+ },
20
+ "skip-check": {
21
+ "char": "s",
22
+ "description": "skips the checking of the parent directory for a shopify theme",
23
+ "name": "skip-check",
24
+ "allowNo": false,
25
+ "type": "boolean"
26
+ }
27
+ },
28
+ "hasDynamicHelp": false,
29
+ "hiddenAliases": [],
30
+ "id": "create-app",
31
+ "pluginAlias": "@firenet-designs/fnd-cli",
32
+ "pluginName": "@firenet-designs/fnd-cli",
33
+ "pluginType": "core",
34
+ "strict": true,
35
+ "enableJsonFlag": false,
36
+ "isESM": true,
37
+ "relativePath": [
38
+ "dist",
39
+ "commands",
40
+ "create-app.js"
41
+ ]
42
+ }
43
+ },
44
+ "version": "1.0.1"
45
+ }
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "@firenet-designs/fnd-cli",
3
+ "description": "A new CLI generated with oclif",
4
+ "version": "1.0.1",
5
+ "author": "Cole Denslow",
6
+ "bin": {
7
+ "fnd-cli": "bin/run.js"
8
+ },
9
+ "bugs": "https://github.com/FireNet-Designs/fnd-cli/issues",
10
+ "dependencies": {
11
+ "@oclif/core": "^4",
12
+ "@oclif/plugin-help": "^6",
13
+ "@oclif/plugin-plugins": "^5",
14
+ "chalk": "^5.6.2",
15
+ "ora": "^9.3.0",
16
+ "simple-git": "^3.32.2"
17
+ },
18
+ "devDependencies": {
19
+ "@eslint/compat": "^1",
20
+ "@oclif/prettier-config": "^0.2.1",
21
+ "@oclif/test": "^4",
22
+ "@types/chai": "^4",
23
+ "@types/mocha": "^10",
24
+ "@types/node": "^18.19.130",
25
+ "chai": "^4",
26
+ "eslint": "^9",
27
+ "eslint-config-oclif": "^6",
28
+ "eslint-config-prettier": "^10",
29
+ "mocha": "^10",
30
+ "oclif": "^4",
31
+ "shx": "^0.3.3",
32
+ "ts-node": "^10",
33
+ "typescript": "^5"
34
+ },
35
+ "engines": {
36
+ "node": ">=18.0.0"
37
+ },
38
+ "files": [
39
+ "./bin",
40
+ "./dist",
41
+ "./oclif.manifest.json"
42
+ ],
43
+ "homepage": "https://github.com/FireNet-Designs/fnd-cli",
44
+ "keywords": [
45
+ "oclif"
46
+ ],
47
+ "license": "MIT",
48
+ "main": "dist/index.js",
49
+ "type": "module",
50
+ "oclif": {
51
+ "bin": "fnd-cli",
52
+ "dirname": "fnd-cli",
53
+ "commands": "./dist/commands",
54
+ "hooks": {
55
+ "init": [
56
+ "./dist/hooks/init/check-for-updates"
57
+ ]
58
+ },
59
+ "plugins": [
60
+ "@oclif/plugin-help"
61
+ ],
62
+ "topicSeparator": " ",
63
+ "topics": {
64
+ "hello": {
65
+ "description": "Say hello to the world and others"
66
+ }
67
+ }
68
+ },
69
+ "repository": {
70
+ "type": "git",
71
+ "url": "git+https://github.com/FireNet-Designs/fnd-cli.git"
72
+ },
73
+ "scripts": {
74
+ "build": "shx rm -rf dist && tsc -b",
75
+ "lint": "eslint",
76
+ "postpack": "shx rm -f oclif.manifest.json",
77
+ "posttest": "npm run lint",
78
+ "prepack": "oclif manifest && oclif readme",
79
+ "version": "oclif readme && git add README.md",
80
+ "release": "npm run build && npm run prepack && npm publish --access public"
81
+ },
82
+ "types": "dist/index.d.ts"
83
+ }