@duonghieu0712z/create-tauri-vue-template 0.0.3
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/LICENSE +21 -0
- package/README.md +74 -0
- package/assets/vscode-settings.json +16 -0
- package/dist/bin/index.js +83 -0
- package/dist/cli/color.js +25 -0
- package/dist/cli/files.js +18 -0
- package/dist/cli/project.js +48 -0
- package/dist/cli/prompts.js +44 -0
- package/dist/cli/release-workflow.js +13 -0
- package/dist/cli/scaffold.js +30 -0
- package/dist/cli/text.js +24 -0
- package/dist/cli/types.js +1 -0
- package/dist/scripts/prepare-template.js +67 -0
- package/package.json +39 -0
- package/template/.editorconfig +8 -0
- package/template/.gitattributes +5 -0
- package/template/.github/dependabot.yml +38 -0
- package/template/.github/workflows/code-quality.yml +70 -0
- package/template/.github/workflows/release.yml +112 -0
- package/template/.husky/pre-commit +7 -0
- package/template/.oxfmtrc.json +36 -0
- package/template/.oxlintrc.json +24 -0
- package/template/.vscode/extensions.json +11 -0
- package/template/CHANGELOG.md +12 -0
- package/template/LICENSE +21 -0
- package/template/README.md +126 -0
- package/template/components.json +24 -0
- package/template/eslint.config.ts +32 -0
- package/template/index.html +14 -0
- package/template/package.json +74 -0
- package/template/pnpm-lock.yaml +3953 -0
- package/template/pnpm-workspace.yaml +3 -0
- package/template/public/favicon.ico +0 -0
- package/template/rust-toolchain.toml +2 -0
- package/template/scripts/bump-version.js +42 -0
- package/template/scripts/rename-app.js +137 -0
- package/template/scripts/utils.js +59 -0
- package/template/src/App.vue +71 -0
- package/template/src/assets/fonts/Geist/Geist-Italic[wght].woff2 +0 -0
- package/template/src/assets/fonts/Geist/Geist[wght].woff2 +0 -0
- package/template/src/assets/images/tauri.svg +6 -0
- package/template/src/assets/images/vite.svg +1 -0
- package/template/src/assets/images/vue.svg +1 -0
- package/template/src/generated/auto-import.d.ts +139 -0
- package/template/src/generated/bindings.ts +9 -0
- package/template/src/lib/utils.ts +8 -0
- package/template/src/main.ts +9 -0
- package/template/src/styles/fonts.css +15 -0
- package/template/src/styles/globals.css +130 -0
- package/template/src/vite-env.d.ts +7 -0
- package/template/src-tauri/Cargo.lock +6328 -0
- package/template/src-tauri/Cargo.toml +41 -0
- package/template/src-tauri/build.rs +3 -0
- package/template/src-tauri/capabilities/default.json +7 -0
- package/template/src-tauri/icons/128x128.png +0 -0
- package/template/src-tauri/icons/128x128@2x.png +0 -0
- package/template/src-tauri/icons/32x32.png +0 -0
- package/template/src-tauri/icons/Square107x107Logo.png +0 -0
- package/template/src-tauri/icons/Square142x142Logo.png +0 -0
- package/template/src-tauri/icons/Square150x150Logo.png +0 -0
- package/template/src-tauri/icons/Square284x284Logo.png +0 -0
- package/template/src-tauri/icons/Square30x30Logo.png +0 -0
- package/template/src-tauri/icons/Square310x310Logo.png +0 -0
- package/template/src-tauri/icons/Square44x44Logo.png +0 -0
- package/template/src-tauri/icons/Square71x71Logo.png +0 -0
- package/template/src-tauri/icons/Square89x89Logo.png +0 -0
- package/template/src-tauri/icons/StoreLogo.png +0 -0
- package/template/src-tauri/icons/icon.icns +0 -0
- package/template/src-tauri/icons/icon.ico +0 -0
- package/template/src-tauri/icons/icon.png +0 -0
- package/template/src-tauri/src/command/mod.rs +13 -0
- package/template/src-tauri/src/lib.rs +54 -0
- package/template/src-tauri/src/main.rs +6 -0
- package/template/src-tauri/tauri.conf.json +37 -0
- package/template/tsconfig.app.json +11 -0
- package/template/tsconfig.json +11 -0
- package/template/tsconfig.node.json +11 -0
- package/template/vite.config.ts +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 duonghieu0712z
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Create Tauri Vue Template
|
|
2
|
+
|
|
3
|
+
Create a Tauri 2 + Vue 3 desktop app from the `tauri-vue-template` starter.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Run the interactive scaffold:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm create @duonghieu0712z/tauri-vue-template@latest
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Pass values directly when needed:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm create @duonghieu0712z/tauri-vue-template@latest my-app --name "My App" --id "com.example.my-app" --author "Your Name"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The CLI copies the bundled template into the target directory, updates the package name, Tauri product name, application identifier, Rust crate names, release workflow metadata, and VS Code workspace settings.
|
|
20
|
+
|
|
21
|
+
## Development
|
|
22
|
+
|
|
23
|
+
Install dependencies:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm install
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The CLI source lives in `src/` and is compiled to `dist/`. The published package runs the compiled JavaScript entry at `dist/bin/index.js`.
|
|
30
|
+
|
|
31
|
+
Check formatting and linting:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm build
|
|
35
|
+
pnpm format
|
|
36
|
+
pnpm lint
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Apply automatic fixes:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pnpm format:fix
|
|
43
|
+
pnpm lint:fix
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Local Test
|
|
47
|
+
|
|
48
|
+
Run the CLI from this repository:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pnpm build
|
|
52
|
+
node dist/bin/index.js
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Run without prompts:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pnpm build
|
|
59
|
+
node dist/bin/index.js --name "My App" --id "com.example.my-app" --author "Your Name"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Publish
|
|
63
|
+
|
|
64
|
+
Build and inspect the npm package tarball contents through the `prepack` script. This compiles TypeScript to `dist/` and prepares the bundled template before packing:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pnpm pack --dry-run
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Publish the package:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pnpm publish --access public
|
|
74
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"explorer.fileNesting.enabled": true,
|
|
3
|
+
"explorer.fileNesting.patterns": {
|
|
4
|
+
"tsconfig.json": "tsconfig.*.json, env.d.ts, typed-router.d.ts",
|
|
5
|
+
"vite.config.*": "jsconfig*, vitest.config.*, cypress.config.*, playwright.config.*",
|
|
6
|
+
"package.json": "package-lock.json, pnpm*, .yarnrc*, yarn*, .eslint*, eslint*, .oxlint*, oxlint*, .oxfmt*, .prettier*, prettier*, .editorconfig"
|
|
7
|
+
},
|
|
8
|
+
"editor.codeActionsOnSave": {
|
|
9
|
+
"source.fixAll": "explicit"
|
|
10
|
+
},
|
|
11
|
+
"editor.formatOnSave": true,
|
|
12
|
+
"editor.defaultFormatter": "oxc.oxc-vscode",
|
|
13
|
+
"[rust]": {
|
|
14
|
+
"editor.defaultFormatter": "rust-lang.rust-analyzer"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { basename, dirname, relative, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { cac } from 'cac';
|
|
6
|
+
import { color, printIntro } from '../cli/color.js';
|
|
7
|
+
import { promptForMissingOptions } from '../cli/prompts.js';
|
|
8
|
+
import { scaffoldProject } from '../cli/scaffold.js';
|
|
9
|
+
import { requiredValue, toKebabCase, toSnakeCase, toTitleCase } from '../cli/text.js';
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
const packageJson = require('../../package.json');
|
|
12
|
+
function parseCommand() {
|
|
13
|
+
const cli = cac('create-tauri-vue-template');
|
|
14
|
+
cli.usage('[project-dir] [options]')
|
|
15
|
+
.option('--name <name>', 'Application display name')
|
|
16
|
+
.option('--package <name>', 'Package name')
|
|
17
|
+
.option('--id <identifier>', 'Tauri application identifier')
|
|
18
|
+
.option('--author <name>', 'Project author')
|
|
19
|
+
.help()
|
|
20
|
+
.version(packageJson.version);
|
|
21
|
+
const parsed = cli.parse(process.argv, { run: false });
|
|
22
|
+
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
if (process.argv.includes('--version') || process.argv.includes('-v')) {
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
const options = parsed.options;
|
|
29
|
+
return {
|
|
30
|
+
projectDir: parsed.args[0],
|
|
31
|
+
options: {
|
|
32
|
+
name: options.name,
|
|
33
|
+
package: options.package,
|
|
34
|
+
id: options.id,
|
|
35
|
+
author: options.author,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async function main() {
|
|
40
|
+
const { projectDir, options } = parseCommand();
|
|
41
|
+
if (!projectDir && !options.name && (!process.stdin.isTTY || !process.stdout.isTTY)) {
|
|
42
|
+
throw new Error('Usage: pnpm create @duonghieu0712z/tauri-vue-template@latest [project-dir] --name "My App" --id "com.example.my-app"');
|
|
43
|
+
}
|
|
44
|
+
printIntro();
|
|
45
|
+
const defaultAppName = projectDir ? toTitleCase(basename(projectDir)) : 'Tauri Vue App';
|
|
46
|
+
const resolvedOptions = await promptForMissingOptions(options, {
|
|
47
|
+
appName: defaultAppName,
|
|
48
|
+
});
|
|
49
|
+
const appName = requiredValue(resolvedOptions.name?.trim() || defaultAppName, 'App name');
|
|
50
|
+
const packageName = requiredValue(resolvedOptions.package?.trim() || toKebabCase(appName), 'Package name');
|
|
51
|
+
const targetArg = projectDir || packageName;
|
|
52
|
+
const targetDir = resolve(process.cwd(), targetArg);
|
|
53
|
+
const crateName = toKebabCase(packageName);
|
|
54
|
+
const crateLibName = `${toSnakeCase(crateName)}_lib`;
|
|
55
|
+
const identifier = requiredValue(resolvedOptions.id?.trim() || `com.example.${packageName}`, 'Tauri identifier');
|
|
56
|
+
const author = resolvedOptions.author?.trim();
|
|
57
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
58
|
+
const packageDir = resolve(currentDir, '..', '..');
|
|
59
|
+
await scaffoldProject(targetDir, packageDir, {
|
|
60
|
+
appName,
|
|
61
|
+
packageName,
|
|
62
|
+
crateName,
|
|
63
|
+
crateLibName,
|
|
64
|
+
identifier,
|
|
65
|
+
author,
|
|
66
|
+
});
|
|
67
|
+
const relativeTargetDir = relative(process.cwd(), targetDir) || '.';
|
|
68
|
+
console.log('');
|
|
69
|
+
console.log(`${color.green('Created')} ${appName} in ${color.cyan(relativeTargetDir)}`);
|
|
70
|
+
console.log('');
|
|
71
|
+
console.log(color.yellow('Next steps:'));
|
|
72
|
+
console.log(` ${color.cyan(`cd ${relativeTargetDir}`)}`);
|
|
73
|
+
console.log(` ${color.cyan('pnpm install')}`);
|
|
74
|
+
console.log(` ${color.cyan('pnpm start')}`);
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
await main();
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
81
|
+
console.error(message);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import colors from 'picocolors';
|
|
2
|
+
export const color = {
|
|
3
|
+
cyan: colors.cyan,
|
|
4
|
+
dim: colors.dim,
|
|
5
|
+
green: colors.green,
|
|
6
|
+
yellow: colors.yellow,
|
|
7
|
+
};
|
|
8
|
+
export function printIntro() {
|
|
9
|
+
if (!process.stdout.isTTY) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
console.log('');
|
|
13
|
+
console.log(color.cyan('Tauri Vue Template'));
|
|
14
|
+
console.log(color.dim('Scaffold a Tauri 2 + Vue 3 desktop app.'));
|
|
15
|
+
console.log('');
|
|
16
|
+
}
|
|
17
|
+
export function printSection(title) {
|
|
18
|
+
if (!process.stdout.isTTY) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
console.log(color.yellow(title));
|
|
22
|
+
}
|
|
23
|
+
export function formatPrompt(label, defaultValue) {
|
|
24
|
+
return `${label} ${color.dim(`(${defaultValue})`)}: `;
|
|
25
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
export async function readJson(path) {
|
|
3
|
+
return JSON.parse(await readFile(path, 'utf8'));
|
|
4
|
+
}
|
|
5
|
+
export async function writeJson(path, value) {
|
|
6
|
+
await writeFile(path, `${JSON.stringify(value, null, 4)}\n`);
|
|
7
|
+
}
|
|
8
|
+
export function replaceOne(content, pattern, replacement, description) {
|
|
9
|
+
if (!pattern.test(content)) {
|
|
10
|
+
throw new Error(`Could not update ${description}`);
|
|
11
|
+
}
|
|
12
|
+
return content.replace(pattern, replacement);
|
|
13
|
+
}
|
|
14
|
+
export async function updateText(path, replacements) {
|
|
15
|
+
const content = await readFile(path, 'utf8');
|
|
16
|
+
const updatedContent = replacements.reduce((currentContent, [pattern, replacement]) => replaceOne(currentContent, pattern, replacement, path), content);
|
|
17
|
+
await writeFile(path, updatedContent);
|
|
18
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { readJson, updateText, writeJson } from './files.js';
|
|
3
|
+
import { updateReleaseWorkflow } from './release-workflow.js';
|
|
4
|
+
export async function renameProject(targetDir, identity) {
|
|
5
|
+
const packageJsonPath = resolve(targetDir, 'package.json');
|
|
6
|
+
const packageJson = await readJson(packageJsonPath);
|
|
7
|
+
packageJson.name = identity.packageName;
|
|
8
|
+
if (identity.author) {
|
|
9
|
+
packageJson.author = identity.author;
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
delete packageJson.author;
|
|
13
|
+
}
|
|
14
|
+
await writeJson(packageJsonPath, packageJson);
|
|
15
|
+
const tauriConfigPath = resolve(targetDir, 'src-tauri', 'tauri.conf.json');
|
|
16
|
+
const tauriConfig = await readJson(tauriConfigPath);
|
|
17
|
+
tauriConfig.productName = identity.appName;
|
|
18
|
+
tauriConfig.identifier = identity.identifier;
|
|
19
|
+
tauriConfig.app.windows = tauriConfig.app.windows.map((window) => ({
|
|
20
|
+
...window,
|
|
21
|
+
title: window.title ? identity.appName : window.title,
|
|
22
|
+
}));
|
|
23
|
+
if (identity.author) {
|
|
24
|
+
tauriConfig.bundle.publisher = identity.author;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
delete tauriConfig.bundle.publisher;
|
|
28
|
+
}
|
|
29
|
+
await writeJson(tauriConfigPath, tauriConfig);
|
|
30
|
+
await updateText(resolve(targetDir, 'index.html'), [
|
|
31
|
+
[/<title>.*<\/title>/, `<title>${identity.appName}</title>`],
|
|
32
|
+
]);
|
|
33
|
+
await updateText(resolve(targetDir, 'src-tauri', 'Cargo.toml'), [
|
|
34
|
+
[/^name = "([^"]+)"/m, `name = "${identity.crateName}"`],
|
|
35
|
+
[/^description = "([^"]+)"/m, `description = "${identity.appName}"`],
|
|
36
|
+
[/^(\[lib\]\s+[\s\S]*?^name = )"([^"]+)"/m, `$1"${identity.crateLibName}"`],
|
|
37
|
+
]);
|
|
38
|
+
await updateText(resolve(targetDir, 'src-tauri', 'Cargo.toml'), [
|
|
39
|
+
[
|
|
40
|
+
/^authors = \[[^\]]*\]/m,
|
|
41
|
+
identity.author ? `authors = ["${identity.author}"]` : 'authors = []',
|
|
42
|
+
],
|
|
43
|
+
]);
|
|
44
|
+
await updateText(resolve(targetDir, 'src-tauri', 'src', 'main.rs'), [
|
|
45
|
+
[/[a-zA-Z0-9_]+_lib::run\(\)/, `${identity.crateLibName}::run()`],
|
|
46
|
+
]);
|
|
47
|
+
await updateReleaseWorkflow(resolve(targetDir, '.github', 'workflows', 'release.yml'), identity);
|
|
48
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline/promises';
|
|
2
|
+
import { formatPrompt, printSection, color } from './color.js';
|
|
3
|
+
import { toKebabCase } from './text.js';
|
|
4
|
+
export async function promptForMissingOptions(options, defaults) {
|
|
5
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
6
|
+
const appName = options.name?.trim() || defaults.appName;
|
|
7
|
+
const packageName = options.package?.trim() || toKebabCase(appName);
|
|
8
|
+
return {
|
|
9
|
+
name: appName,
|
|
10
|
+
package: packageName,
|
|
11
|
+
id: options.id?.trim() || `com.example.${packageName}`,
|
|
12
|
+
author: options.author?.trim() || '',
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
const prompt = createInterface({
|
|
16
|
+
input: process.stdin,
|
|
17
|
+
output: process.stdout,
|
|
18
|
+
});
|
|
19
|
+
try {
|
|
20
|
+
printSection('Project identity');
|
|
21
|
+
const appName = options.name?.trim() ||
|
|
22
|
+
(await prompt.question(formatPrompt('App name', defaults.appName))).trim() ||
|
|
23
|
+
defaults.appName;
|
|
24
|
+
const defaultPackageName = toKebabCase(appName);
|
|
25
|
+
const packageName = options.package?.trim() ||
|
|
26
|
+
(await prompt.question(formatPrompt('Package name', defaultPackageName))).trim() ||
|
|
27
|
+
defaultPackageName;
|
|
28
|
+
const defaultIdentifier = `com.example.${packageName}`;
|
|
29
|
+
const identifier = options.id?.trim() ||
|
|
30
|
+
(await prompt.question(formatPrompt('Tauri identifier', defaultIdentifier))).trim() ||
|
|
31
|
+
defaultIdentifier;
|
|
32
|
+
const author = options.author?.trim() ||
|
|
33
|
+
(await prompt.question(`Author ${color.dim('(optional)')}: `)).trim();
|
|
34
|
+
return {
|
|
35
|
+
name: appName,
|
|
36
|
+
package: packageName,
|
|
37
|
+
id: identifier,
|
|
38
|
+
author,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
prompt.close();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { replaceOne } from './files.js';
|
|
3
|
+
export async function updateReleaseWorkflow(path, identity) {
|
|
4
|
+
let content = await readFile(path, 'utf8');
|
|
5
|
+
content = replaceOne(content, /^(\s*releaseName:\s*).*$/m, `$1${identity.appName} v__VERSION__`, path);
|
|
6
|
+
if (/^\s*releaseAssetNamePattern:\s*/m.test(content)) {
|
|
7
|
+
content = replaceOne(content, /^(\s*releaseAssetNamePattern:\s*).*$/m, `$1${identity.packageName}_[version]_[arch][setup][ext]`, path);
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
content = replaceOne(content, /^(\s*prerelease:\s*false)$/m, `$1\n releaseAssetNamePattern: ${identity.packageName}_[version]_[arch][setup][ext]`, path);
|
|
11
|
+
}
|
|
12
|
+
await writeFile(path, content);
|
|
13
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { access, copyFile, cp, mkdir, readdir } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { renameProject } from './project.js';
|
|
4
|
+
export async function ensureEmptyTarget(path) {
|
|
5
|
+
await mkdir(path, { recursive: true });
|
|
6
|
+
const entries = await readdir(path);
|
|
7
|
+
if (entries.length > 0) {
|
|
8
|
+
throw new Error(`Target directory is not empty: ${path}`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export async function ensureTemplate(templateDir) {
|
|
12
|
+
try {
|
|
13
|
+
await access(templateDir);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
await import('../scripts/prepare-template.js');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function writeEditorSettings(targetDir, packageDir) {
|
|
20
|
+
await mkdir(resolve(targetDir, '.vscode'), { recursive: true });
|
|
21
|
+
await copyFile(resolve(packageDir, 'assets', 'vscode-settings.json'), resolve(targetDir, '.vscode', 'settings.json'));
|
|
22
|
+
}
|
|
23
|
+
export async function scaffoldProject(targetDir, packageDir, identity) {
|
|
24
|
+
const templateDir = resolve(packageDir, 'template');
|
|
25
|
+
await ensureTemplate(templateDir);
|
|
26
|
+
await ensureEmptyTarget(targetDir);
|
|
27
|
+
await cp(templateDir, targetDir, { recursive: true });
|
|
28
|
+
await writeEditorSettings(targetDir, packageDir);
|
|
29
|
+
await renameProject(targetDir, identity);
|
|
30
|
+
}
|
package/dist/cli/text.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function toKebabCase(value) {
|
|
2
|
+
return value
|
|
3
|
+
.trim()
|
|
4
|
+
.replace(/['"]/g, '')
|
|
5
|
+
.replace(/[^a-zA-Z0-9]+/g, '-')
|
|
6
|
+
.replace(/^-+|-+$/g, '')
|
|
7
|
+
.toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
export function toSnakeCase(value) {
|
|
10
|
+
return toKebabCase(value).replaceAll('-', '_');
|
|
11
|
+
}
|
|
12
|
+
export function toTitleCase(value) {
|
|
13
|
+
return toKebabCase(value)
|
|
14
|
+
.split('-')
|
|
15
|
+
.filter(Boolean)
|
|
16
|
+
.map((word) => `${word[0].toUpperCase()}${word.slice(1)}`)
|
|
17
|
+
.join(' ');
|
|
18
|
+
}
|
|
19
|
+
export function requiredValue(value, name) {
|
|
20
|
+
if (!value) {
|
|
21
|
+
throw new Error(`${name} cannot be empty`);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createWriteStream } from 'node:fs';
|
|
3
|
+
import { cp, mkdir, readdir, rm } from 'node:fs/promises';
|
|
4
|
+
import { dirname, resolve } from 'node:path';
|
|
5
|
+
import { pipeline } from 'node:stream/promises';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { extract } from 'tar';
|
|
8
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const packageDir = resolve(currentDir, '..', '..');
|
|
10
|
+
const templateDir = resolve(packageDir, 'template');
|
|
11
|
+
const archivePath = resolve(packageDir, '.tmp-template.tar.gz');
|
|
12
|
+
const templateRepo = process.env.TAURI_VUE_TEMPLATE_REPO || 'duonghieu0712z/tauri-vue-template';
|
|
13
|
+
const templateRef = process.env.TAURI_VUE_TEMPLATE_REF || 'main';
|
|
14
|
+
const templateSourceDir = process.env.TAURI_VUE_TEMPLATE_SOURCE
|
|
15
|
+
? resolve(process.env.TAURI_VUE_TEMPLATE_SOURCE)
|
|
16
|
+
: null;
|
|
17
|
+
const excludedPaths = new Set([
|
|
18
|
+
'.git',
|
|
19
|
+
'.eslintcache',
|
|
20
|
+
'dist',
|
|
21
|
+
'node_modules',
|
|
22
|
+
'packages',
|
|
23
|
+
'src-tauri/target',
|
|
24
|
+
]);
|
|
25
|
+
function normalizePath(path) {
|
|
26
|
+
return path.replaceAll('\\', '/');
|
|
27
|
+
}
|
|
28
|
+
function shouldInclude(relativePath) {
|
|
29
|
+
return !excludedPaths.has(normalizePath(relativePath));
|
|
30
|
+
}
|
|
31
|
+
async function copyTemplateSource(sourceDir) {
|
|
32
|
+
await mkdir(templateDir, { recursive: true });
|
|
33
|
+
await readdir(sourceDir).then((entries) => Promise.all(entries
|
|
34
|
+
.filter((entry) => shouldInclude(entry))
|
|
35
|
+
.map((entry) => cp(resolve(sourceDir, entry), resolve(templateDir, entry), {
|
|
36
|
+
recursive: true,
|
|
37
|
+
filter: (source) => shouldInclude(normalizePath(source).slice(normalizePath(sourceDir).length + 1)),
|
|
38
|
+
}))));
|
|
39
|
+
}
|
|
40
|
+
async function downloadTemplateArchive() {
|
|
41
|
+
const archiveUrl = `https://github.com/${templateRepo}/archive/${templateRef}.tar.gz`;
|
|
42
|
+
const response = await fetch(archiveUrl);
|
|
43
|
+
if (!response.ok || !response.body) {
|
|
44
|
+
throw new Error(`Could not download template archive: ${archiveUrl}`);
|
|
45
|
+
}
|
|
46
|
+
await pipeline(response.body, createWriteStream(archivePath));
|
|
47
|
+
}
|
|
48
|
+
async function extractTemplateArchive() {
|
|
49
|
+
await mkdir(templateDir, { recursive: true });
|
|
50
|
+
await extract({
|
|
51
|
+
file: archivePath,
|
|
52
|
+
cwd: templateDir,
|
|
53
|
+
strip: 1,
|
|
54
|
+
filter: (path) => shouldInclude(path.split('/').slice(1).join('/')),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
await rm(templateDir, { recursive: true, force: true });
|
|
58
|
+
await rm(archivePath, { force: true });
|
|
59
|
+
if (templateSourceDir) {
|
|
60
|
+
await copyTemplateSource(templateSourceDir);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
await downloadTemplateArchive();
|
|
64
|
+
await extractTemplateArchive();
|
|
65
|
+
await rm(archivePath, { force: true });
|
|
66
|
+
}
|
|
67
|
+
console.log(`Prepared template at ${templateDir}`);
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@duonghieu0712z/create-tauri-vue-template",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Create a Tauri 2 + Vue 3 desktop app from the tauri-vue-template starter.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"create-tauri-vue-template": "dist/bin/index.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"assets",
|
|
10
|
+
"dist",
|
|
11
|
+
"template"
|
|
12
|
+
],
|
|
13
|
+
"type": "module",
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"cac": "^7.0.0",
|
|
19
|
+
"picocolors": "^1.1.1",
|
|
20
|
+
"tar": "^7.5.1"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^25.9.1",
|
|
24
|
+
"oxfmt": "^0.51.0",
|
|
25
|
+
"oxlint": "^1.66.0",
|
|
26
|
+
"typescript": "~6.0.3"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
33
|
+
"build": "pnpm clean && tsc -p tsconfig.json",
|
|
34
|
+
"format": "oxfmt --check .",
|
|
35
|
+
"format:fix": "oxfmt .",
|
|
36
|
+
"lint": "oxlint .",
|
|
37
|
+
"lint:fix": "oxlint --fix ."
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
updates:
|
|
4
|
+
- package-ecosystem: npm
|
|
5
|
+
directory: /
|
|
6
|
+
schedule:
|
|
7
|
+
interval: weekly
|
|
8
|
+
day: monday
|
|
9
|
+
time: '02:00'
|
|
10
|
+
timezone: Etc/UTC
|
|
11
|
+
open-pull-requests-limit: 10
|
|
12
|
+
groups:
|
|
13
|
+
npm-minor-and-patch:
|
|
14
|
+
update-types:
|
|
15
|
+
- minor
|
|
16
|
+
- patch
|
|
17
|
+
|
|
18
|
+
- package-ecosystem: cargo
|
|
19
|
+
directory: /src-tauri
|
|
20
|
+
schedule:
|
|
21
|
+
interval: weekly
|
|
22
|
+
day: monday
|
|
23
|
+
time: '02:30'
|
|
24
|
+
timezone: Etc/UTC
|
|
25
|
+
open-pull-requests-limit: 10
|
|
26
|
+
groups:
|
|
27
|
+
cargo-minor-and-patch:
|
|
28
|
+
update-types:
|
|
29
|
+
- minor
|
|
30
|
+
- patch
|
|
31
|
+
|
|
32
|
+
- package-ecosystem: github-actions
|
|
33
|
+
directory: /
|
|
34
|
+
schedule:
|
|
35
|
+
interval: weekly
|
|
36
|
+
day: monday
|
|
37
|
+
time: '03:00'
|
|
38
|
+
timezone: Etc/UTC
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
name: Code Quality
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
env:
|
|
13
|
+
NODE_VERSION: '24'
|
|
14
|
+
PNPM_VERSION: '11'
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
frontend:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout repository
|
|
22
|
+
uses: actions/checkout@v6
|
|
23
|
+
|
|
24
|
+
- name: Setup pnpm
|
|
25
|
+
uses: pnpm/action-setup@v6
|
|
26
|
+
with:
|
|
27
|
+
version: ${{ env.PNPM_VERSION }}
|
|
28
|
+
|
|
29
|
+
- name: Setup Node.js
|
|
30
|
+
uses: actions/setup-node@v6
|
|
31
|
+
with:
|
|
32
|
+
node-version: ${{ env.NODE_VERSION }}
|
|
33
|
+
cache: pnpm
|
|
34
|
+
|
|
35
|
+
- name: Install dependencies
|
|
36
|
+
run: pnpm install --frozen-lockfile
|
|
37
|
+
|
|
38
|
+
- name: Check formatting
|
|
39
|
+
run: pnpm format
|
|
40
|
+
|
|
41
|
+
- name: Check lint
|
|
42
|
+
run: pnpm lint
|
|
43
|
+
|
|
44
|
+
rust:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
|
|
47
|
+
steps:
|
|
48
|
+
- name: Checkout repository
|
|
49
|
+
uses: actions/checkout@v6
|
|
50
|
+
|
|
51
|
+
- name: Install Linux dependencies
|
|
52
|
+
run: |
|
|
53
|
+
sudo apt-get update
|
|
54
|
+
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
|
55
|
+
|
|
56
|
+
- name: Setup Rust
|
|
57
|
+
uses: actions-rust-lang/setup-rust-toolchain@v1
|
|
58
|
+
with:
|
|
59
|
+
components: rustfmt, clippy
|
|
60
|
+
|
|
61
|
+
- name: Rust cache
|
|
62
|
+
uses: swatinem/rust-cache@v2
|
|
63
|
+
with:
|
|
64
|
+
workspaces: './src-tauri -> target'
|
|
65
|
+
|
|
66
|
+
- name: Check formatting
|
|
67
|
+
run: cargo fmt --manifest-path src-tauri/Cargo.toml -- --check
|
|
68
|
+
|
|
69
|
+
- name: Check lint
|
|
70
|
+
run: cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets --all-features -- -D warnings
|