create-jsonlink 1.0.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/README.md +60 -0
- package/bin/create-jsonlink.js +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -0
- package/dist/initExisting.d.ts +17 -0
- package/dist/initExisting.d.ts.map +1 -0
- package/dist/initExisting.js +219 -0
- package/dist/initExisting.js.map +1 -0
- package/dist/scaffoldStarter.d.ts +9 -0
- package/dist/scaffoldStarter.d.ts.map +1 -0
- package/dist/scaffoldStarter.js +542 -0
- package/dist/scaffoldStarter.js.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# create-jsonlink
|
|
2
|
+
|
|
3
|
+
The official starter and initialization CLI for **[JSON Link](https://json-link.pages.dev)**.
|
|
4
|
+
|
|
5
|
+
Scaffold a high-performance React + Vite + TypeScript application preconfigured with the JSON Link localization workspace, or initialize JSON Link into any existing React project with a single command.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
### Option 1: Create a New Localized App (Zero Setup)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm create jsonlink my-i18n-app
|
|
15
|
+
# or
|
|
16
|
+
npx create-jsonlink my-i18n-app
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then start your app:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
cd my-i18n-app
|
|
23
|
+
npm install
|
|
24
|
+
npm run dev
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- **App**: `http://localhost:5173`
|
|
28
|
+
- **JSON Link Live Dashboard**: `http://localhost:5173/__jsonlink`
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
### Option 2: Initialize in an Existing Vite Project
|
|
33
|
+
|
|
34
|
+
Run inside any existing React + Vite repository root:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx create-jsonlink
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This will automatically:
|
|
41
|
+
1. Detect your package manager (`npm`, `pnpm`, `bun`, `yarn`).
|
|
42
|
+
2. Inject `jsonLink()` into your `vite.config.ts`.
|
|
43
|
+
3. Add `@jsonlink/vite-plugin` to `devDependencies`.
|
|
44
|
+
4. Scaffold `src/locales/` (`en.json`, `my.json`, `translations.d.ts`, `i18n.ts`).
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## What is Included?
|
|
49
|
+
|
|
50
|
+
- **Vite Devtool Plugin**: Two-way disk synchronization with instant Vite HMR.
|
|
51
|
+
- **Embedded Dev Dashboard**: Full spreadsheet localization table at `http://localhost:5173/__jsonlink`.
|
|
52
|
+
- **Zero Runtime Overhead**: The devtool runs strictly during `vite dev` and tree-shakes completely out of production builds.
|
|
53
|
+
- **Type-Safe Autocomplete**: Full TypeScript definition generation (`translations.d.ts`) for key names and variable interpolations.
|
|
54
|
+
- **Lossless Myanmar Support**: Built-in Unicode and Zawgyi font handling.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
MIT © [Pyae Phyo Maung](https://github.com/pyaephyomaungdev/json-link)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { initExistingProject, findViteConfig, detectPackageManager } from './initExisting.js';
|
|
2
|
+
import { scaffoldStarterProject } from './scaffoldStarter.js';
|
|
3
|
+
export { initExistingProject, scaffoldStarterProject, detectPackageManager, findViteConfig };
|
|
4
|
+
export declare function runCli(argv?: string[]): void;
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9F,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,cAAc,EAAE,CAAC;AAE7F,wBAAgB,MAAM,CAAC,IAAI,WAAwB,GAAG,IAAI,CAqGzD"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { initExistingProject, findViteConfig, detectPackageManager } from './initExisting.js';
|
|
3
|
+
import { scaffoldStarterProject } from './scaffoldStarter.js';
|
|
4
|
+
export { initExistingProject, scaffoldStarterProject, detectPackageManager, findViteConfig };
|
|
5
|
+
export function runCli(argv = process.argv.slice(2)) {
|
|
6
|
+
const args = argv.filter(a => !a.startsWith('-'));
|
|
7
|
+
const flags = new Set(argv.filter(a => a.startsWith('-')));
|
|
8
|
+
if (flags.has('--help') || flags.has('-h')) {
|
|
9
|
+
console.log(`
|
|
10
|
+
\x1b[36m⚡ create-jsonlink\x1b[0m — Instant local-first localization for React + Vite
|
|
11
|
+
|
|
12
|
+
\x1b[1mUsage:\x1b[0m
|
|
13
|
+
npm create jsonlink [project-name]
|
|
14
|
+
npx create-jsonlink [project-name]
|
|
15
|
+
|
|
16
|
+
\x1b[1mModes:\x1b[0m
|
|
17
|
+
1. \x1b[32mNew Project Mode:\x1b[0m
|
|
18
|
+
npx create-jsonlink my-i18n-app
|
|
19
|
+
Scaffolds a complete React + TypeScript + Vite project preconfigured with JSON Link.
|
|
20
|
+
|
|
21
|
+
2. \x1b[32mExisting Project Mode:\x1b[0m
|
|
22
|
+
npx create-jsonlink
|
|
23
|
+
(Run inside an existing Vite project root to inject plugin and scaffold locales)
|
|
24
|
+
`);
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
const projectName = args[0];
|
|
28
|
+
const cwd = process.cwd();
|
|
29
|
+
// Mode 1: Project name provided -> Scaffold new starter app
|
|
30
|
+
if (projectName) {
|
|
31
|
+
const targetDir = path.resolve(cwd, projectName);
|
|
32
|
+
console.log(`\n\x1b[36m⚡ Creating a new JSON Link localized project in\x1b[0m \x1b[1m${targetDir}\x1b[0m...\n`);
|
|
33
|
+
const result = scaffoldStarterProject({ targetDir, projectName });
|
|
34
|
+
if (!result.success) {
|
|
35
|
+
console.error(`\x1b[31m✖ ${result.message}\x1b[0m\n`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
console.log(`\x1b[32m✔\x1b[0m Project created successfully!`);
|
|
39
|
+
console.log(`\x1b[32m✔\x1b[0m React 19 + TypeScript + Vite configured`);
|
|
40
|
+
console.log(`\x1b[32m✔\x1b[0m JSON Link plugin and reactive i18n client scaffolded\n`);
|
|
41
|
+
console.log(`\x1b[1mNext steps:\x1b[0m`);
|
|
42
|
+
console.log(` cd \x1b[36m${projectName}\x1b[0m`);
|
|
43
|
+
console.log(` \x1b[36mnpm install\x1b[0m`);
|
|
44
|
+
console.log(` \x1b[36mnpm run dev\x1b[0m\n`);
|
|
45
|
+
console.log(`Open in browser:`);
|
|
46
|
+
console.log(` App: \x1b[36mhttp://localhost:5173\x1b[0m`);
|
|
47
|
+
console.log(` Live Devtool: \x1b[36mhttp://localhost:5173/__jsonlink\x1b[0m\n`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Mode 2: No argument provided -> Check if current folder is a Vite project
|
|
51
|
+
const existingViteConfig = findViteConfig(cwd);
|
|
52
|
+
if (existingViteConfig) {
|
|
53
|
+
console.log(`\n\x1b[36m⚡ Existing Vite project detected. Initializing JSON Link...\x1b[0m\n`);
|
|
54
|
+
const pm = detectPackageManager(cwd);
|
|
55
|
+
const result = initExistingProject(cwd);
|
|
56
|
+
if (!result.success) {
|
|
57
|
+
console.error(`\x1b[31m✖ ${result.message}\x1b[0m\n`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
console.log(`\x1b[32m✔\x1b[0m Injected JSON Link plugin into \x1b[1m${result.viteConfigFile}\x1b[0m`);
|
|
61
|
+
if (result.localesCreated) {
|
|
62
|
+
console.log(`\x1b[32m✔\x1b[0m Created \x1b[1msrc/locales/\x1b[0m (en.json, my.json, translations.d.ts, i18n.ts)`);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.log(`\x1b[33mℹ\x1b[0m Existing \x1b[1msrc/locales/\x1b[0m preserved`);
|
|
66
|
+
}
|
|
67
|
+
if (result.packageUpdated) {
|
|
68
|
+
console.log(`\x1b[32m✔\x1b[0m Added \x1b[1m@jsonlink/vite-plugin\x1b[0m to devDependencies in package.json`);
|
|
69
|
+
}
|
|
70
|
+
console.log(`\n\x1b[32m\x1b[1m🎉 JSON Link Dev Dashboard is ready!\x1b[0m\n`);
|
|
71
|
+
console.log(`Run your dev server:`);
|
|
72
|
+
console.log(` \x1b[36m${pm === 'npm' ? 'npm run dev' : `${pm} dev`}\x1b[0m\n`);
|
|
73
|
+
console.log(`Open translation workspace:`);
|
|
74
|
+
console.log(` 👉 \x1b[36mhttp://localhost:5173/__jsonlink\x1b[0m\n`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// Mode 3: Neither project name nor Vite config in current dir -> Default starter in ./jsonlink-app
|
|
78
|
+
const defaultDir = 'jsonlink-app';
|
|
79
|
+
const targetDir = path.resolve(cwd, defaultDir);
|
|
80
|
+
console.log(`\n\x1b[36m⚡ No Vite project detected in current directory.\x1b[0m`);
|
|
81
|
+
console.log(`Scaffolding a new starter app in \x1b[1m./${defaultDir}\x1b[0m...\n`);
|
|
82
|
+
const result = scaffoldStarterProject({ targetDir, projectName: defaultDir });
|
|
83
|
+
if (!result.success) {
|
|
84
|
+
console.error(`\x1b[31m✖ ${result.message}\x1b[0m\n`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
console.log(`\x1b[32m✔\x1b[0m Project created successfully!`);
|
|
88
|
+
console.log(`\n\x1b[1mNext steps:\x1b[0m`);
|
|
89
|
+
console.log(` cd \x1b[36m${defaultDir}\x1b[0m`);
|
|
90
|
+
console.log(` \x1b[36mnpm install\x1b[0m`);
|
|
91
|
+
console.log(` \x1b[36mnpm run dev\x1b[0m\n`);
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface InitExistingResult {
|
|
2
|
+
success: boolean;
|
|
3
|
+
viteConfigFile?: string;
|
|
4
|
+
localesCreated?: boolean;
|
|
5
|
+
packageUpdated?: boolean;
|
|
6
|
+
message?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function detectPackageManager(targetDir: string): 'pnpm' | 'yarn' | 'bun' | 'npm';
|
|
9
|
+
export declare function findViteConfig(targetDir: string): string | null;
|
|
10
|
+
export declare function injectVitePlugin(configContent: string): {
|
|
11
|
+
content: string;
|
|
12
|
+
changed: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function scaffoldLocales(targetDir: string): boolean;
|
|
15
|
+
export declare function ensurePluginDependency(targetDir: string): boolean;
|
|
16
|
+
export declare function initExistingProject(targetDir?: string): InitExistingResult;
|
|
17
|
+
//# sourceMappingURL=initExisting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initExisting.d.ts","sourceRoot":"","sources":["../src/initExisting.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAKvF;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB/D;AAED,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAqB7F;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAyI1D;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAkBjE;AAED,wBAAgB,mBAAmB,CAAC,SAAS,SAAgB,GAAG,kBAAkB,CAwBjF"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export function detectPackageManager(targetDir) {
|
|
4
|
+
if (fs.existsSync(path.join(targetDir, 'pnpm-lock.yaml')))
|
|
5
|
+
return 'pnpm';
|
|
6
|
+
if (fs.existsSync(path.join(targetDir, 'bun.lockb')) || fs.existsSync(path.join(targetDir, 'bun.lock')))
|
|
7
|
+
return 'bun';
|
|
8
|
+
if (fs.existsSync(path.join(targetDir, 'yarn.lock')))
|
|
9
|
+
return 'yarn';
|
|
10
|
+
return 'npm';
|
|
11
|
+
}
|
|
12
|
+
export function findViteConfig(targetDir) {
|
|
13
|
+
const possibleFiles = [
|
|
14
|
+
'vite.config.ts',
|
|
15
|
+
'vite.config.js',
|
|
16
|
+
'vite.config.mjs',
|
|
17
|
+
'vite.config.mts',
|
|
18
|
+
];
|
|
19
|
+
for (const file of possibleFiles) {
|
|
20
|
+
const fullPath = path.join(targetDir, file);
|
|
21
|
+
if (fs.existsSync(fullPath)) {
|
|
22
|
+
return fullPath;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
export function injectVitePlugin(configContent) {
|
|
28
|
+
if (configContent.includes('jsonLink(') || configContent.includes('@jsonlink/vite-plugin')) {
|
|
29
|
+
return { content: configContent, changed: false };
|
|
30
|
+
}
|
|
31
|
+
let updated = configContent;
|
|
32
|
+
const importStatement = "import { jsonLink } from '@jsonlink/vite-plugin';\n";
|
|
33
|
+
updated = importStatement + updated;
|
|
34
|
+
const pluginsRegex = /plugins\s*:\s*\[/;
|
|
35
|
+
if (pluginsRegex.test(updated)) {
|
|
36
|
+
updated = updated.replace(pluginsRegex, 'plugins: [\n jsonLink(),');
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const defineRegex = /defineConfig\s*\(\s*\{/;
|
|
40
|
+
if (defineRegex.test(updated)) {
|
|
41
|
+
updated = updated.replace(defineRegex, 'defineConfig({\n plugins: [jsonLink()],');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { content: updated, changed: true };
|
|
45
|
+
}
|
|
46
|
+
export function scaffoldLocales(targetDir) {
|
|
47
|
+
const localesDir = path.join(targetDir, 'src', 'locales');
|
|
48
|
+
if (fs.existsSync(localesDir)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
fs.mkdirSync(localesDir, { recursive: true });
|
|
52
|
+
const enData = {
|
|
53
|
+
"app.title": "My Localized App",
|
|
54
|
+
"app.description": "A modern multilingual application with JSON Link and React.",
|
|
55
|
+
"auth.welcome": "Welcome back, {username}!",
|
|
56
|
+
"common.save": "Save Changes",
|
|
57
|
+
"common.cancel": "Cancel",
|
|
58
|
+
"common.switch_language": "Language",
|
|
59
|
+
"common.english": "English",
|
|
60
|
+
"common.myanmar": "မြန်မာ"
|
|
61
|
+
};
|
|
62
|
+
fs.writeFileSync(path.join(localesDir, 'en.json'), JSON.stringify(enData, null, 2) + '\n');
|
|
63
|
+
const myData = {
|
|
64
|
+
"app.title": "ကျွန်ုပ်၏အက်ပ်",
|
|
65
|
+
"app.description": "JSON Link နှင့် React ဖြင့် တည်ဆောက်ထားသော ဘာသာစကားစုံသုံး အက်ပ်။",
|
|
66
|
+
"auth.welcome": "ကြိုဆိုပါတယ် {username}!",
|
|
67
|
+
"common.save": "သိမ်းဆည်းမည်",
|
|
68
|
+
"common.cancel": "မလုပ်တော့ပါ",
|
|
69
|
+
"common.switch_language": "ဘာသာစကား",
|
|
70
|
+
"common.english": "English",
|
|
71
|
+
"common.myanmar": "မြန်မာ"
|
|
72
|
+
};
|
|
73
|
+
fs.writeFileSync(path.join(localesDir, 'my.json'), JSON.stringify(myData, null, 2) + '\n');
|
|
74
|
+
const defaultKeys = Object.keys(enData);
|
|
75
|
+
const dtsContent = `export type SupportedLanguage = "en" | "my";
|
|
76
|
+
|
|
77
|
+
export type TranslationKey =
|
|
78
|
+
${defaultKeys.map(k => ` | ${JSON.stringify(k)}`).join('\n')};
|
|
79
|
+
|
|
80
|
+
export interface TranslationDictionary {
|
|
81
|
+
[key: string]: string;
|
|
82
|
+
}
|
|
83
|
+
`;
|
|
84
|
+
fs.writeFileSync(path.join(localesDir, 'translations.d.ts'), dtsContent);
|
|
85
|
+
const i18nContent = `import { useSyncExternalStore } from 'react';
|
|
86
|
+
import type { SupportedLanguage, TranslationKey } from './translations';
|
|
87
|
+
import en from './en.json';
|
|
88
|
+
import my from './my.json';
|
|
89
|
+
|
|
90
|
+
export const SUPPORTED_LANGUAGES: SupportedLanguage[] = ['en', 'my'];
|
|
91
|
+
|
|
92
|
+
export const resources: Record<SupportedLanguage, Record<string, any>> = {
|
|
93
|
+
'en': en,
|
|
94
|
+
'my': my,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
let currentLanguage: SupportedLanguage = 'en';
|
|
98
|
+
const listeners = new Set<() => void>();
|
|
99
|
+
|
|
100
|
+
function notify() {
|
|
101
|
+
listeners.forEach((listener) => listener());
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function setLanguage(lang: SupportedLanguage): void {
|
|
105
|
+
if (resources[lang]) {
|
|
106
|
+
currentLanguage = lang;
|
|
107
|
+
notify();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function getLanguage(): SupportedLanguage {
|
|
112
|
+
return currentLanguage;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function resolveValue(obj: Record<string, any>, key: string): string | undefined {
|
|
116
|
+
if (!obj) return undefined;
|
|
117
|
+
if (obj[key] !== undefined) return String(obj[key]);
|
|
118
|
+
const parts = key.split('.');
|
|
119
|
+
let current: any = obj;
|
|
120
|
+
for (const part of parts) {
|
|
121
|
+
if (current && typeof current === 'object' && part in current) {
|
|
122
|
+
current = current[part];
|
|
123
|
+
} else {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return typeof current === 'string' ? current : undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function t(
|
|
131
|
+
key: TranslationKey | (string & {}),
|
|
132
|
+
params?: Record<string, string | number>,
|
|
133
|
+
lang?: SupportedLanguage
|
|
134
|
+
): string {
|
|
135
|
+
const activeLang = lang || currentLanguage;
|
|
136
|
+
const dict = resources[activeLang] || resources['en'] || {};
|
|
137
|
+
let text = resolveValue(dict, key);
|
|
138
|
+
|
|
139
|
+
if (text === undefined) {
|
|
140
|
+
text = resolveValue(resources['en'] || {}, key) ?? key;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (params && typeof text === 'string') {
|
|
144
|
+
return text.replace(/\\{([a-zA-Z0-9_]+)\\}/g, (_, varName) => {
|
|
145
|
+
return params[varName] !== undefined ? String(params[varName]) : \`{\${varName}}\`;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return text;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function useTranslation() {
|
|
153
|
+
const lang = useSyncExternalStore(
|
|
154
|
+
(callback) => {
|
|
155
|
+
listeners.add(callback);
|
|
156
|
+
return () => listeners.delete(callback);
|
|
157
|
+
},
|
|
158
|
+
() => currentLanguage
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
t,
|
|
163
|
+
language: lang,
|
|
164
|
+
setLanguage,
|
|
165
|
+
languages: SUPPORTED_LANGUAGES,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (import.meta.hot) {
|
|
170
|
+
import.meta.hot.accept((newModule) => {
|
|
171
|
+
if (newModule) notify();
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
`;
|
|
175
|
+
fs.writeFileSync(path.join(localesDir, 'i18n.ts'), i18nContent);
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
export function ensurePluginDependency(targetDir) {
|
|
179
|
+
const pkgPath = path.join(targetDir, 'package.json');
|
|
180
|
+
if (!fs.existsSync(pkgPath))
|
|
181
|
+
return false;
|
|
182
|
+
try {
|
|
183
|
+
const raw = fs.readFileSync(pkgPath, 'utf-8');
|
|
184
|
+
const pkg = JSON.parse(raw);
|
|
185
|
+
pkg.devDependencies = pkg.devDependencies || {};
|
|
186
|
+
if (!pkg.devDependencies['@jsonlink/vite-plugin']) {
|
|
187
|
+
pkg.devDependencies['@jsonlink/vite-plugin'] = '^1.0.0';
|
|
188
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// Ignore JSON parse errors
|
|
194
|
+
}
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
export function initExistingProject(targetDir = process.cwd()) {
|
|
198
|
+
const configPath = findViteConfig(targetDir);
|
|
199
|
+
if (!configPath) {
|
|
200
|
+
return {
|
|
201
|
+
success: false,
|
|
202
|
+
message: 'No Vite configuration found (vite.config.ts/js).',
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const rawConfig = fs.readFileSync(configPath, 'utf-8');
|
|
206
|
+
const { content: updatedConfig, changed } = injectVitePlugin(rawConfig);
|
|
207
|
+
if (changed) {
|
|
208
|
+
fs.writeFileSync(configPath, updatedConfig, 'utf-8');
|
|
209
|
+
}
|
|
210
|
+
const localesCreated = scaffoldLocales(targetDir);
|
|
211
|
+
const packageUpdated = ensurePluginDependency(targetDir);
|
|
212
|
+
return {
|
|
213
|
+
success: true,
|
|
214
|
+
viteConfigFile: path.basename(configPath),
|
|
215
|
+
localesCreated,
|
|
216
|
+
packageUpdated,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=initExisting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initExisting.js","sourceRoot":"","sources":["../src/initExisting.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffoldStarter.d.ts","sourceRoot":"","sources":["../src/scaffoldStarter.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CA6iBtG"}
|
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export function scaffoldStarterProject(options) {
|
|
4
|
+
const { targetDir, projectName } = options;
|
|
5
|
+
if (fs.existsSync(targetDir)) {
|
|
6
|
+
const files = fs.readdirSync(targetDir);
|
|
7
|
+
if (files.length > 0) {
|
|
8
|
+
return {
|
|
9
|
+
success: false,
|
|
10
|
+
message: `Target directory "${projectName}" is not empty. Please use an empty directory or existing Vite project.`,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
// 1. package.json
|
|
18
|
+
const packageJson = {
|
|
19
|
+
name: projectName,
|
|
20
|
+
private: true,
|
|
21
|
+
version: "0.1.0",
|
|
22
|
+
type: "module",
|
|
23
|
+
scripts: {
|
|
24
|
+
dev: "vite",
|
|
25
|
+
build: "tsc -b && vite build",
|
|
26
|
+
preview: "vite preview"
|
|
27
|
+
},
|
|
28
|
+
dependencies: {
|
|
29
|
+
react: "^19.0.0",
|
|
30
|
+
"react-dom": "^19.0.0",
|
|
31
|
+
"lucide-react": "^1.16.0"
|
|
32
|
+
},
|
|
33
|
+
devDependencies: {
|
|
34
|
+
"@jsonlink/vite-plugin": "^1.0.0",
|
|
35
|
+
"@types/react": "^19.0.10",
|
|
36
|
+
"@types/react-dom": "^19.0.4",
|
|
37
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
38
|
+
typescript: "~5.7.2",
|
|
39
|
+
vite: "^6.2.0"
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(packageJson, null, 2) + '\n');
|
|
43
|
+
// 2. vite.config.ts
|
|
44
|
+
const viteConfig = `import { defineConfig } from 'vite';
|
|
45
|
+
import react from '@vitejs/plugin-react';
|
|
46
|
+
import { jsonLink } from '@jsonlink/vite-plugin';
|
|
47
|
+
|
|
48
|
+
// https://vite.dev/config/
|
|
49
|
+
export default defineConfig({
|
|
50
|
+
plugins: [
|
|
51
|
+
react(),
|
|
52
|
+
jsonLink({
|
|
53
|
+
localesDir: './src/locales',
|
|
54
|
+
route: '/__jsonlink',
|
|
55
|
+
}),
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
`;
|
|
59
|
+
fs.writeFileSync(path.join(targetDir, 'vite.config.ts'), viteConfig);
|
|
60
|
+
// 3. tsconfig.json, tsconfig.app.json, tsconfig.node.json
|
|
61
|
+
const tsconfig = {
|
|
62
|
+
files: [],
|
|
63
|
+
references: [
|
|
64
|
+
{ path: "./tsconfig.app.json" },
|
|
65
|
+
{ path: "./tsconfig.node.json" }
|
|
66
|
+
]
|
|
67
|
+
};
|
|
68
|
+
fs.writeFileSync(path.join(targetDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2) + '\n');
|
|
69
|
+
const tsconfigApp = {
|
|
70
|
+
compilerOptions: {
|
|
71
|
+
target: "ES2020",
|
|
72
|
+
useDefineForClassFields: true,
|
|
73
|
+
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
74
|
+
module: "ESNext",
|
|
75
|
+
skipLibCheck: true,
|
|
76
|
+
moduleResolution: "bundler",
|
|
77
|
+
allowImportingTsExtensions: false,
|
|
78
|
+
isolatedModules: true,
|
|
79
|
+
moduleDetection: "force",
|
|
80
|
+
noEmit: true,
|
|
81
|
+
jsx: "react-jsx",
|
|
82
|
+
strict: true,
|
|
83
|
+
noUnusedLocals: true,
|
|
84
|
+
noUnusedParameters: true,
|
|
85
|
+
noFallthroughCasesInSwitch: true
|
|
86
|
+
},
|
|
87
|
+
include: ["src"]
|
|
88
|
+
};
|
|
89
|
+
fs.writeFileSync(path.join(targetDir, 'tsconfig.app.json'), JSON.stringify(tsconfigApp, null, 2) + '\n');
|
|
90
|
+
const tsconfigNode = {
|
|
91
|
+
compilerOptions: {
|
|
92
|
+
target: "ES2022",
|
|
93
|
+
lib: ["ES2023"],
|
|
94
|
+
module: "ESNext",
|
|
95
|
+
skipLibCheck: true,
|
|
96
|
+
moduleResolution: "bundler",
|
|
97
|
+
isolatedModules: true,
|
|
98
|
+
moduleDetection: "force",
|
|
99
|
+
noEmit: true,
|
|
100
|
+
strict: true
|
|
101
|
+
},
|
|
102
|
+
include: ["vite.config.ts"]
|
|
103
|
+
};
|
|
104
|
+
fs.writeFileSync(path.join(targetDir, 'tsconfig.node.json'), JSON.stringify(tsconfigNode, null, 2) + '\n');
|
|
105
|
+
// 4. index.html
|
|
106
|
+
const indexHtml = `<!doctype html>
|
|
107
|
+
<html lang="en">
|
|
108
|
+
<head>
|
|
109
|
+
<meta charset="UTF-8" />
|
|
110
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
111
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
112
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
113
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Noto+Sans+Myanmar:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
114
|
+
<title>JSON Link App</title>
|
|
115
|
+
</head>
|
|
116
|
+
<body>
|
|
117
|
+
<div id="root"></div>
|
|
118
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
119
|
+
</body>
|
|
120
|
+
</html>
|
|
121
|
+
`;
|
|
122
|
+
fs.writeFileSync(path.join(targetDir, 'index.html'), indexHtml);
|
|
123
|
+
// 5. src directory
|
|
124
|
+
const srcDir = path.join(targetDir, 'src');
|
|
125
|
+
fs.mkdirSync(srcDir, { recursive: true });
|
|
126
|
+
// 6. src/index.css
|
|
127
|
+
const indexCss = `:root {
|
|
128
|
+
font-family: 'Inter', 'Noto Sans Myanmar', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
129
|
+
line-height: 1.5;
|
|
130
|
+
font-weight: 400;
|
|
131
|
+
color-scheme: dark light;
|
|
132
|
+
color: rgba(255, 255, 255, 0.87);
|
|
133
|
+
background-color: #0f172a;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
body {
|
|
137
|
+
margin: 0;
|
|
138
|
+
display: flex;
|
|
139
|
+
place-items: center;
|
|
140
|
+
min-width: 320px;
|
|
141
|
+
min-height: 100vh;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#root {
|
|
145
|
+
width: 100%;
|
|
146
|
+
max-width: 960px;
|
|
147
|
+
margin: 0 auto;
|
|
148
|
+
padding: 2rem;
|
|
149
|
+
text-align: center;
|
|
150
|
+
}
|
|
151
|
+
`;
|
|
152
|
+
fs.writeFileSync(path.join(srcDir, 'index.css'), indexCss);
|
|
153
|
+
// 7. src/App.css
|
|
154
|
+
const appCss = `.container {
|
|
155
|
+
display: flex;
|
|
156
|
+
flex-direction: column;
|
|
157
|
+
align-items: center;
|
|
158
|
+
gap: 2rem;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.badge {
|
|
162
|
+
display: inline-flex;
|
|
163
|
+
align-items: center;
|
|
164
|
+
gap: 0.5rem;
|
|
165
|
+
background: rgba(30, 41, 59, 0.8);
|
|
166
|
+
border: 1px solid rgba(148, 163, 184, 0.2);
|
|
167
|
+
padding: 0.35rem 1rem;
|
|
168
|
+
border-radius: 9999px;
|
|
169
|
+
font-size: 0.875rem;
|
|
170
|
+
color: #38bdf8;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.title {
|
|
174
|
+
font-size: 2.75rem;
|
|
175
|
+
font-weight: 800;
|
|
176
|
+
margin: 0;
|
|
177
|
+
background: linear-gradient(135deg, #f8fafc 0%, #94a3b8 100%);
|
|
178
|
+
-webkit-background-clip: text;
|
|
179
|
+
-webkit-text-fill-color: transparent;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.subtitle {
|
|
183
|
+
font-size: 1.15rem;
|
|
184
|
+
color: #94a3b8;
|
|
185
|
+
margin: 0;
|
|
186
|
+
max-width: 600px;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.lang-picker {
|
|
190
|
+
display: inline-flex;
|
|
191
|
+
background: #1e293b;
|
|
192
|
+
padding: 0.25rem;
|
|
193
|
+
border-radius: 0.75rem;
|
|
194
|
+
border: 1px solid #334155;
|
|
195
|
+
gap: 0.25rem;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.lang-btn {
|
|
199
|
+
padding: 0.5rem 1.25rem;
|
|
200
|
+
border-radius: 0.5rem;
|
|
201
|
+
border: none;
|
|
202
|
+
background: transparent;
|
|
203
|
+
color: #94a3b8;
|
|
204
|
+
font-weight: 500;
|
|
205
|
+
cursor: pointer;
|
|
206
|
+
transition: all 0.2s;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.lang-btn.active {
|
|
210
|
+
background: #3b82f6;
|
|
211
|
+
color: #ffffff;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.card-grid {
|
|
215
|
+
display: grid;
|
|
216
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
217
|
+
gap: 1.5rem;
|
|
218
|
+
width: 100%;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.card {
|
|
222
|
+
background: #1e293b;
|
|
223
|
+
border: 1px solid #334155;
|
|
224
|
+
border-radius: 1rem;
|
|
225
|
+
padding: 1.75rem;
|
|
226
|
+
text-align: left;
|
|
227
|
+
display: flex;
|
|
228
|
+
flex-direction: column;
|
|
229
|
+
justify-content: space-between;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.card h3 {
|
|
233
|
+
margin-top: 0;
|
|
234
|
+
margin-bottom: 0.5rem;
|
|
235
|
+
color: #f8fafc;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.card p {
|
|
239
|
+
color: #94a3b8;
|
|
240
|
+
font-size: 0.95rem;
|
|
241
|
+
line-height: 1.6;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.btn-action {
|
|
245
|
+
display: inline-flex;
|
|
246
|
+
align-items: center;
|
|
247
|
+
justify-content: center;
|
|
248
|
+
gap: 0.5rem;
|
|
249
|
+
background: #2563eb;
|
|
250
|
+
color: #ffffff;
|
|
251
|
+
padding: 0.75rem 1.25rem;
|
|
252
|
+
border-radius: 0.5rem;
|
|
253
|
+
text-decoration: none;
|
|
254
|
+
font-weight: 600;
|
|
255
|
+
border: none;
|
|
256
|
+
cursor: pointer;
|
|
257
|
+
margin-top: 1rem;
|
|
258
|
+
transition: background 0.2s;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.btn-action:hover {
|
|
262
|
+
background: #1d4ed8;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.btn-outline {
|
|
266
|
+
background: transparent;
|
|
267
|
+
border: 1px solid #475569;
|
|
268
|
+
color: #f8fafc;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.btn-outline:hover {
|
|
272
|
+
background: #334155;
|
|
273
|
+
}
|
|
274
|
+
`;
|
|
275
|
+
fs.writeFileSync(path.join(srcDir, 'App.css'), appCss);
|
|
276
|
+
// 8. src/main.tsx
|
|
277
|
+
const mainTsx = `import React from 'react';
|
|
278
|
+
import ReactDOM from 'react-dom/client';
|
|
279
|
+
import App from './App';
|
|
280
|
+
import './index.css';
|
|
281
|
+
|
|
282
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
283
|
+
<React.StrictMode>
|
|
284
|
+
<App />
|
|
285
|
+
</React.StrictMode>
|
|
286
|
+
);
|
|
287
|
+
`;
|
|
288
|
+
fs.writeFileSync(path.join(srcDir, 'main.tsx'), mainTsx);
|
|
289
|
+
// 9. src/App.tsx
|
|
290
|
+
const appTsx = `import { useState } from 'react';
|
|
291
|
+
import { useTranslation, SupportedLanguage } from './locales/i18n';
|
|
292
|
+
import './App.css';
|
|
293
|
+
|
|
294
|
+
export default function App() {
|
|
295
|
+
const { t, language, setLanguage, languages } = useTranslation();
|
|
296
|
+
const [count, setCount] = useState(0);
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
<div className="container">
|
|
300
|
+
<div className="badge">
|
|
301
|
+
<span>⚡</span>
|
|
302
|
+
<span>JSON Link Local-First Platform</span>
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
<h1 className="title">{t('app.title')}</h1>
|
|
306
|
+
<p className="subtitle">{t('app.subtitle')}</p>
|
|
307
|
+
|
|
308
|
+
{/* Language Switcher */}
|
|
309
|
+
<div className="lang-picker">
|
|
310
|
+
{languages.map((lang: SupportedLanguage) => (
|
|
311
|
+
<button
|
|
312
|
+
key={lang}
|
|
313
|
+
className={\`lang-btn \${language === lang ? 'active' : ''}\`}
|
|
314
|
+
onClick={() => setLanguage(lang)}
|
|
315
|
+
>
|
|
316
|
+
{lang === 'en' ? 'English' : 'မြန်မာ'}
|
|
317
|
+
</button>
|
|
318
|
+
))}
|
|
319
|
+
</div>
|
|
320
|
+
|
|
321
|
+
<div className="card-grid">
|
|
322
|
+
{/* Interactive Demo Card */}
|
|
323
|
+
<div className="card">
|
|
324
|
+
<div>
|
|
325
|
+
<h3>{t('counter.label')}</h3>
|
|
326
|
+
<p>{t('auth.welcome', { username: 'Developer' })}</p>
|
|
327
|
+
<p>{t('counter.clicks', { count })}</p>
|
|
328
|
+
</div>
|
|
329
|
+
<button className="btn-action btn-outline" onClick={() => setCount((c) => c + 1)}>
|
|
330
|
+
{t('counter.button')}
|
|
331
|
+
</button>
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
{/* Devtool Dashboard Card */}
|
|
335
|
+
<div className="card">
|
|
336
|
+
<div>
|
|
337
|
+
<h3>{t('dashboard.card.title')}</h3>
|
|
338
|
+
<p>{t('dashboard.card.desc')}</p>
|
|
339
|
+
</div>
|
|
340
|
+
<a
|
|
341
|
+
href="/__jsonlink"
|
|
342
|
+
target="_blank"
|
|
343
|
+
rel="noopener noreferrer"
|
|
344
|
+
className="btn-action"
|
|
345
|
+
>
|
|
346
|
+
{t('dashboard.card.button')} ↗
|
|
347
|
+
</a>
|
|
348
|
+
</div>
|
|
349
|
+
</div>
|
|
350
|
+
</div>
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
`;
|
|
354
|
+
fs.writeFileSync(path.join(srcDir, 'App.tsx'), appTsx);
|
|
355
|
+
// 10. src/locales/
|
|
356
|
+
const localesDir = path.join(srcDir, 'locales');
|
|
357
|
+
fs.mkdirSync(localesDir, { recursive: true });
|
|
358
|
+
const enJson = {
|
|
359
|
+
"app.title": "JSON Link Starter",
|
|
360
|
+
"app.subtitle": "Instant local-first localization for modern React applications with Vite HMR.",
|
|
361
|
+
"auth.welcome": "Welcome back, {username}!",
|
|
362
|
+
"counter.label": "Reactive Localization Demo",
|
|
363
|
+
"counter.clicks": "You have clicked {count} times.",
|
|
364
|
+
"counter.button": "Increment Counter",
|
|
365
|
+
"dashboard.card.title": "Live Translation Workspace",
|
|
366
|
+
"dashboard.card.desc": "Inspect, edit, and organize all your translation keys side-by-side with two-way disk synchronization.",
|
|
367
|
+
"dashboard.card.button": "Open Dev Dashboard",
|
|
368
|
+
"common.switch_language": "Language",
|
|
369
|
+
"common.english": "English",
|
|
370
|
+
"common.myanmar": "မြန်မာ"
|
|
371
|
+
};
|
|
372
|
+
fs.writeFileSync(path.join(localesDir, 'en.json'), JSON.stringify(enJson, null, 2) + '\n');
|
|
373
|
+
const myJson = {
|
|
374
|
+
"app.title": "JSON Link စတင်အသုံးပြုခြင်း",
|
|
375
|
+
"app.subtitle": "React အက်ပ်များအတွက် Vite HMR ဖြင့် ချက်ချင်းချိတ်ဆက်အလုပ်လုပ်သော ဘာသာစကားစုံသုံး စနစ်။",
|
|
376
|
+
"auth.welcome": "ကြိုဆိုပါတယ် {username}!",
|
|
377
|
+
"counter.label": "အပြန်အလှန်တုံ့ပြန်နိုင်သော ကောင်တာ",
|
|
378
|
+
"counter.clicks": "သင် {count} ကြိမ် နှိပ်ခဲ့ပြီးပါပြီ။",
|
|
379
|
+
"counter.button": "ကောင်တာ တိုးမည်",
|
|
380
|
+
"dashboard.card.title": "တိုက်ရိုက် ဘာသာပြန် ဒက်ရှ်ဘုတ်",
|
|
381
|
+
"dashboard.card.desc": "စာသားများကို စာရင်းဇယားအတိုင်း ယှဉ်တွဲပြင်ဆင်ပြီး Disk ပေါ်သို့ တိုက်ရိုက်သိမ်းဆည်းနိုင်ပါသည်။",
|
|
382
|
+
"dashboard.card.button": "ဒက်ရှ်ဘုတ် ဖွင့်မည်",
|
|
383
|
+
"common.switch_language": "ဘာသာစကား",
|
|
384
|
+
"common.english": "English",
|
|
385
|
+
"common.myanmar": "မြန်မာ"
|
|
386
|
+
};
|
|
387
|
+
fs.writeFileSync(path.join(localesDir, 'my.json'), JSON.stringify(myJson, null, 2) + '\n');
|
|
388
|
+
const defaultKeys = Object.keys(enJson);
|
|
389
|
+
const translationsDts = `export type SupportedLanguage = "en" | "my";
|
|
390
|
+
|
|
391
|
+
export type TranslationKey =
|
|
392
|
+
${defaultKeys.map(k => ` | ${JSON.stringify(k)}`).join('\n')};
|
|
393
|
+
|
|
394
|
+
export interface TranslationDictionary {
|
|
395
|
+
[key: string]: string;
|
|
396
|
+
}
|
|
397
|
+
`;
|
|
398
|
+
fs.writeFileSync(path.join(localesDir, 'translations.d.ts'), translationsDts);
|
|
399
|
+
const i18nTs = `import { useSyncExternalStore } from 'react';
|
|
400
|
+
import type { SupportedLanguage, TranslationKey } from './translations';
|
|
401
|
+
import en from './en.json';
|
|
402
|
+
import my from './my.json';
|
|
403
|
+
|
|
404
|
+
export const SUPPORTED_LANGUAGES: SupportedLanguage[] = ['en', 'my'];
|
|
405
|
+
|
|
406
|
+
export const resources: Record<SupportedLanguage, Record<string, any>> = {
|
|
407
|
+
'en': en,
|
|
408
|
+
'my': my,
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
let currentLanguage: SupportedLanguage = 'en';
|
|
412
|
+
const listeners = new Set<() => void>();
|
|
413
|
+
|
|
414
|
+
function notify() {
|
|
415
|
+
listeners.forEach((listener) => listener());
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function setLanguage(lang: SupportedLanguage): void {
|
|
419
|
+
if (resources[lang]) {
|
|
420
|
+
currentLanguage = lang;
|
|
421
|
+
notify();
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export function getLanguage(): SupportedLanguage {
|
|
426
|
+
return currentLanguage;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function resolveValue(obj: Record<string, any>, key: string): string | undefined {
|
|
430
|
+
if (!obj) return undefined;
|
|
431
|
+
if (obj[key] !== undefined) return String(obj[key]);
|
|
432
|
+
const parts = key.split('.');
|
|
433
|
+
let current: any = obj;
|
|
434
|
+
for (const part of parts) {
|
|
435
|
+
if (current && typeof current === 'object' && part in current) {
|
|
436
|
+
current = current[part];
|
|
437
|
+
} else {
|
|
438
|
+
return undefined;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return typeof current === 'string' ? current : undefined;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export function t(
|
|
445
|
+
key: TranslationKey | (string & {}),
|
|
446
|
+
params?: Record<string, string | number>,
|
|
447
|
+
lang?: SupportedLanguage
|
|
448
|
+
): string {
|
|
449
|
+
const activeLang = lang || currentLanguage;
|
|
450
|
+
const dict = resources[activeLang] || resources['en'] || {};
|
|
451
|
+
let text = resolveValue(dict, key);
|
|
452
|
+
|
|
453
|
+
if (text === undefined) {
|
|
454
|
+
text = resolveValue(resources['en'] || {}, key) ?? key;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (params && typeof text === 'string') {
|
|
458
|
+
return text.replace(/\\{([a-zA-Z0-9_]+)\\}/g, (_, varName) => {
|
|
459
|
+
return params[varName] !== undefined ? String(params[varName]) : \`{\${varName}}\`;
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return text;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
export function useTranslation() {
|
|
467
|
+
const lang = useSyncExternalStore(
|
|
468
|
+
(callback) => {
|
|
469
|
+
listeners.add(callback);
|
|
470
|
+
return () => listeners.delete(callback);
|
|
471
|
+
},
|
|
472
|
+
() => currentLanguage
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
return {
|
|
476
|
+
t,
|
|
477
|
+
language: lang,
|
|
478
|
+
setLanguage,
|
|
479
|
+
languages: SUPPORTED_LANGUAGES,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (import.meta.hot) {
|
|
484
|
+
import.meta.hot.accept((newModule) => {
|
|
485
|
+
if (newModule) notify();
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
`;
|
|
489
|
+
fs.writeFileSync(path.join(localesDir, 'i18n.ts'), i18nTs);
|
|
490
|
+
// 11. .gitignore
|
|
491
|
+
const gitignore = `# Logs
|
|
492
|
+
logs
|
|
493
|
+
*.log
|
|
494
|
+
npm-debug.log*
|
|
495
|
+
yarn-debug.log*
|
|
496
|
+
yarn-error.log*
|
|
497
|
+
pnpm-debug.log*
|
|
498
|
+
lerna-debug.log*
|
|
499
|
+
|
|
500
|
+
node_modules
|
|
501
|
+
dist
|
|
502
|
+
dist-ssr
|
|
503
|
+
*.local
|
|
504
|
+
|
|
505
|
+
# Editor directories and files
|
|
506
|
+
.vscode/*
|
|
507
|
+
!.vscode/extensions.json
|
|
508
|
+
.idea
|
|
509
|
+
.DS_Store
|
|
510
|
+
*.suo
|
|
511
|
+
*.ntvs*
|
|
512
|
+
*.njsproj
|
|
513
|
+
*.sln
|
|
514
|
+
*.sw?
|
|
515
|
+
`;
|
|
516
|
+
fs.writeFileSync(path.join(targetDir, '.gitignore'), gitignore);
|
|
517
|
+
// 12. README.md
|
|
518
|
+
const readme = `# ${projectName}
|
|
519
|
+
|
|
520
|
+
A modern React + Vite application preconfigured with **[JSON Link](https://json-link.pages.dev)**.
|
|
521
|
+
|
|
522
|
+
## Quick Start
|
|
523
|
+
|
|
524
|
+
\`\`\`bash
|
|
525
|
+
# Install dependencies
|
|
526
|
+
npm install
|
|
527
|
+
|
|
528
|
+
# Start local development server
|
|
529
|
+
npm run dev
|
|
530
|
+
\`\`\`
|
|
531
|
+
|
|
532
|
+
- App is running at: **\`http://localhost:5173\`**
|
|
533
|
+
- JSON Link Live Dashboard: **\`http://localhost:5173/__jsonlink\`**
|
|
534
|
+
|
|
535
|
+
## Two-Way Disk Synchronization
|
|
536
|
+
|
|
537
|
+
Changes made in the \`/__jsonlink\` web devtool are written directly to \`src/locales/*.json\` on disk with instant Vite HMR.
|
|
538
|
+
`;
|
|
539
|
+
fs.writeFileSync(path.join(targetDir, 'README.md'), readme);
|
|
540
|
+
return { success: true };
|
|
541
|
+
}
|
|
542
|
+
//# sourceMappingURL=scaffoldStarter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffoldStarter.js","sourceRoot":"","sources":["../src/scaffoldStarter.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-jsonlink",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Scaffold a modern React + Vite application preconfigured with JSON Link localization workspace, or initialize JSON Link in an existing project",
|
|
5
|
+
"author": "Pyae Phyo Maung",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"bin": {
|
|
11
|
+
"create-jsonlink": "./bin/create-jsonlink.js"
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/pyaephyomaungdev/json-link.git",
|
|
19
|
+
"directory": "packages/create-jsonlink"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"bin",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc -p tsconfig.json"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"create-jsonlink",
|
|
31
|
+
"jsonlink",
|
|
32
|
+
"json-link",
|
|
33
|
+
"i18n",
|
|
34
|
+
"vite",
|
|
35
|
+
"vite-plugin",
|
|
36
|
+
"react",
|
|
37
|
+
"localization",
|
|
38
|
+
"translation",
|
|
39
|
+
"starter",
|
|
40
|
+
"scaffold",
|
|
41
|
+
"myanmar"
|
|
42
|
+
]
|
|
43
|
+
}
|