notra-editor 1.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.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +262 -0
- package/package.json +53 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
|
|
4
|
+
// src/index.ts
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
|
|
7
|
+
// src/constants.ts
|
|
8
|
+
var CONSTANTS = {
|
|
9
|
+
TARGET_DIR: "components/notra-editor",
|
|
10
|
+
REGISTRY_BASE_URL: "https://raw.githubusercontent.com/levix0501/notra-editor/main/public/r",
|
|
11
|
+
COMPONENT_NAME: "editor"
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// src/registry/fetcher.ts
|
|
15
|
+
async function fetchRegistry(url) {
|
|
16
|
+
try {
|
|
17
|
+
const response = await fetch(url);
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
return {
|
|
20
|
+
success: false,
|
|
21
|
+
error: `Failed to fetch registry: HTTP ${response.status} ${response.statusText}`
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const data = await response.json();
|
|
25
|
+
return {
|
|
26
|
+
success: true,
|
|
27
|
+
data
|
|
28
|
+
};
|
|
29
|
+
} catch (err) {
|
|
30
|
+
const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
|
|
31
|
+
return {
|
|
32
|
+
success: false,
|
|
33
|
+
error: `Failed to fetch registry: ${errorMessage}`
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/utils/conflict.ts
|
|
39
|
+
import { existsSync, readdirSync } from "fs";
|
|
40
|
+
import { join, isAbsolute } from "path";
|
|
41
|
+
function detectConflicts(targetDir, cwd = process.cwd()) {
|
|
42
|
+
const absolutePath = isAbsolute(targetDir) ? targetDir : join(cwd, targetDir);
|
|
43
|
+
if (!existsSync(absolutePath)) {
|
|
44
|
+
return {
|
|
45
|
+
hasConflict: false,
|
|
46
|
+
existingFiles: []
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const files = readdirSync(absolutePath, {
|
|
50
|
+
recursive: true,
|
|
51
|
+
withFileTypes: true
|
|
52
|
+
}).filter((dirent) => dirent.isFile()).map((dirent) => {
|
|
53
|
+
const parentPath = dirent.parentPath || dirent.path;
|
|
54
|
+
const relativePath = parentPath.replace(absolutePath, "").replace(/^[/\\]/, "");
|
|
55
|
+
return relativePath ? join(relativePath, dirent.name) : dirent.name;
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
hasConflict: files.length > 0,
|
|
59
|
+
existingFiles: files
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/utils/copier.ts
|
|
64
|
+
import { writeFileSync, mkdirSync } from "fs";
|
|
65
|
+
import { join as join2, dirname, isAbsolute as isAbsolute2 } from "path";
|
|
66
|
+
async function copyFiles(files, targetDir, cwd = process.cwd()) {
|
|
67
|
+
const copiedFiles = [];
|
|
68
|
+
const absoluteTargetDir = isAbsolute2(targetDir) ? targetDir : join2(cwd, targetDir);
|
|
69
|
+
try {
|
|
70
|
+
for (const file of files) {
|
|
71
|
+
const targetPath = join2(absoluteTargetDir, file.path);
|
|
72
|
+
const targetDirPath = dirname(targetPath);
|
|
73
|
+
mkdirSync(targetDirPath, { recursive: true });
|
|
74
|
+
writeFileSync(targetPath, file.content, "utf-8");
|
|
75
|
+
copiedFiles.push(file.path);
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
success: true,
|
|
79
|
+
copiedFiles
|
|
80
|
+
};
|
|
81
|
+
} catch (err) {
|
|
82
|
+
const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
copiedFiles,
|
|
86
|
+
error: `Failed to copy files: ${errorMessage}`
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/utils/installer.ts
|
|
92
|
+
import { execSync } from "child_process";
|
|
93
|
+
function generateInstallCommand(dependencies) {
|
|
94
|
+
if (dependencies.length === 0) {
|
|
95
|
+
return "pnpm add";
|
|
96
|
+
}
|
|
97
|
+
const packages = dependencies.map((dep) => `${dep.name}@${dep.version}`).join(" ");
|
|
98
|
+
return `pnpm add ${packages}`;
|
|
99
|
+
}
|
|
100
|
+
async function installDependencies(dependencies, cwd = process.cwd()) {
|
|
101
|
+
if (dependencies.length === 0) {
|
|
102
|
+
return { success: true };
|
|
103
|
+
}
|
|
104
|
+
const command = generateInstallCommand(dependencies);
|
|
105
|
+
try {
|
|
106
|
+
execSync(command, {
|
|
107
|
+
cwd,
|
|
108
|
+
stdio: "pipe"
|
|
109
|
+
});
|
|
110
|
+
return { success: true };
|
|
111
|
+
} catch (err) {
|
|
112
|
+
const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
|
|
113
|
+
return {
|
|
114
|
+
success: false,
|
|
115
|
+
error: `Failed to install dependencies: ${errorMessage}`,
|
|
116
|
+
manualCommand: command
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/utils/prompts.ts
|
|
122
|
+
import prompts from "prompts";
|
|
123
|
+
function isInteractive() {
|
|
124
|
+
return process.stdin.isTTY === true;
|
|
125
|
+
}
|
|
126
|
+
async function confirmOverwrite(targetDir) {
|
|
127
|
+
if (!isInteractive()) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
const response = await prompts({
|
|
131
|
+
type: "confirm",
|
|
132
|
+
name: "overwrite",
|
|
133
|
+
message: `Directory "${targetDir}" already exists. Do you want to overwrite it?`,
|
|
134
|
+
initial: false
|
|
135
|
+
});
|
|
136
|
+
if (response.overwrite === void 0) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return response.overwrite;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/utils/ui.ts
|
|
143
|
+
import kleur from "kleur";
|
|
144
|
+
import ora from "ora";
|
|
145
|
+
function createSpinner() {
|
|
146
|
+
const spinner = ora();
|
|
147
|
+
return {
|
|
148
|
+
start(text) {
|
|
149
|
+
spinner.start(text);
|
|
150
|
+
},
|
|
151
|
+
succeed(text) {
|
|
152
|
+
spinner.succeed(text);
|
|
153
|
+
},
|
|
154
|
+
fail(text) {
|
|
155
|
+
spinner.fail(text);
|
|
156
|
+
},
|
|
157
|
+
stop() {
|
|
158
|
+
spinner.stop();
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function success(message) {
|
|
163
|
+
console.log(kleur.green(message));
|
|
164
|
+
}
|
|
165
|
+
function error(message) {
|
|
166
|
+
console.log(kleur.red(message));
|
|
167
|
+
}
|
|
168
|
+
function info(message) {
|
|
169
|
+
console.log(kleur.cyan(message));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/utils/validator.ts
|
|
173
|
+
import { existsSync as existsSync2 } from "fs";
|
|
174
|
+
import { join as join3 } from "path";
|
|
175
|
+
function validateProject(cwd = process.cwd()) {
|
|
176
|
+
const packageJsonPath = join3(cwd, "package.json");
|
|
177
|
+
if (existsSync2(packageJsonPath)) {
|
|
178
|
+
return { valid: true };
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
valid: false,
|
|
182
|
+
error: "package.json not found. Please run this command in a project root directory."
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/commands/init.ts
|
|
187
|
+
function buildRegistryUrl() {
|
|
188
|
+
return `${CONSTANTS.REGISTRY_BASE_URL}/${CONSTANTS.COMPONENT_NAME}.json`;
|
|
189
|
+
}
|
|
190
|
+
async function initCommand(options = {}) {
|
|
191
|
+
const spinner = createSpinner();
|
|
192
|
+
const validation = validateProject();
|
|
193
|
+
if (!validation.valid) {
|
|
194
|
+
error(validation.error || "Project validation failed");
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
const conflicts = detectConflicts(CONSTANTS.TARGET_DIR);
|
|
198
|
+
if (conflicts.hasConflict) {
|
|
199
|
+
if (options.force) {
|
|
200
|
+
info(`Overwriting existing files in ${CONSTANTS.TARGET_DIR}...`);
|
|
201
|
+
} else if (!isInteractive()) {
|
|
202
|
+
error(
|
|
203
|
+
`Directory "${CONSTANTS.TARGET_DIR}" already exists. Use --force to overwrite.`
|
|
204
|
+
);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
} else {
|
|
207
|
+
const shouldOverwrite = await confirmOverwrite(CONSTANTS.TARGET_DIR);
|
|
208
|
+
if (!shouldOverwrite) {
|
|
209
|
+
info("Operation cancelled. No files were modified.");
|
|
210
|
+
process.exit(0);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
spinner.start("Fetching editor registry...");
|
|
215
|
+
const registryUrl = buildRegistryUrl();
|
|
216
|
+
const fetchResult = await fetchRegistry(registryUrl);
|
|
217
|
+
if (!fetchResult.success || !fetchResult.data) {
|
|
218
|
+
spinner.fail("Failed to fetch registry");
|
|
219
|
+
error(
|
|
220
|
+
fetchResult.error || "Network request failed. Please check your internet connection."
|
|
221
|
+
);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
spinner.succeed("Registry fetched successfully");
|
|
225
|
+
const registry = fetchResult.data;
|
|
226
|
+
spinner.start("Copying editor files...");
|
|
227
|
+
const copyResult = await copyFiles(registry.files, CONSTANTS.TARGET_DIR);
|
|
228
|
+
if (!copyResult.success) {
|
|
229
|
+
spinner.fail("Failed to copy files");
|
|
230
|
+
error(
|
|
231
|
+
copyResult.error || "File write failed. Please check directory permissions."
|
|
232
|
+
);
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
spinner.succeed(
|
|
236
|
+
`Copied ${copyResult.copiedFiles.length} files to ${CONSTANTS.TARGET_DIR}`
|
|
237
|
+
);
|
|
238
|
+
if (registry.dependencies.length > 0) {
|
|
239
|
+
spinner.start("Installing dependencies...");
|
|
240
|
+
const installResult = await installDependencies(registry.dependencies);
|
|
241
|
+
if (!installResult.success) {
|
|
242
|
+
spinner.fail("Failed to install dependencies automatically");
|
|
243
|
+
info("Please run the following command manually:");
|
|
244
|
+
info(` ${installResult.manualCommand}`);
|
|
245
|
+
} else {
|
|
246
|
+
spinner.succeed("Dependencies installed successfully");
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
success("\n\u2728 notra-editor initialized successfully!");
|
|
250
|
+
info(`
|
|
251
|
+
Editor files have been copied to: ${CONSTANTS.TARGET_DIR}`);
|
|
252
|
+
info("You can now import the editor in your project:");
|
|
253
|
+
info(` import { Editor } from '@/components/notra-editor/editor';`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// src/index.ts
|
|
257
|
+
var program = new Command();
|
|
258
|
+
program.name("notra-editor").version("1.0.0").description("CLI tool to scaffold notra-editor into your project");
|
|
259
|
+
program.command("init").description("Initialize notra-editor in your project").option("-f, --force", "Overwrite existing files without prompting").action(async (options) => {
|
|
260
|
+
await initCommand({ force: options.force });
|
|
261
|
+
});
|
|
262
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "notra-editor",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.0.0-beta.0",
|
|
5
|
+
"description": "CLI tool to scaffold notra-editor into your project",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/levix0501/notra-editor.git",
|
|
10
|
+
"directory": "packages/cli"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"tiptap",
|
|
17
|
+
"editor",
|
|
18
|
+
"cli",
|
|
19
|
+
"scaffold"
|
|
20
|
+
],
|
|
21
|
+
"bin": {
|
|
22
|
+
"notra-editor": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"commander": "^14.0.0",
|
|
32
|
+
"kleur": "^4.1.5",
|
|
33
|
+
"ora": "^8.2.0",
|
|
34
|
+
"prompts": "^2.4.2"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^20",
|
|
38
|
+
"@types/prompts": "^2.4.9",
|
|
39
|
+
"fast-check": "^3.19.0",
|
|
40
|
+
"tsup": "^8.5.0",
|
|
41
|
+
"typescript": "^5",
|
|
42
|
+
"vitest": "^4.0.18"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsup",
|
|
46
|
+
"dev": "tsup --watch",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"test:watch": "vitest",
|
|
49
|
+
"release": "changeset version",
|
|
50
|
+
"pub:beta": "pnpm build && pnpm publish --no-git-checks --access public --tag beta",
|
|
51
|
+
"pub:release": "pnpm build && pnpm publish --access public"
|
|
52
|
+
}
|
|
53
|
+
}
|