Skip to content

Commit e81cd76

Browse files
committed
fun: Fun mode.
1 parent ef77182 commit e81cd76

14 files changed

Lines changed: 1406 additions & 23 deletions

File tree

packages/cli/assets/error.png

1.77 MB
Loading

packages/cli/binding/index.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,4 +771,5 @@ module.exports.rewritePrettier = nativeBinding.rewritePrettier;
771771
module.exports.rewriteScripts = nativeBinding.rewriteScripts;
772772
module.exports.run = nativeBinding.run;
773773
module.exports.runCommand = nativeBinding.runCommand;
774+
module.exports.takeCheckFailureKind = nativeBinding.takeCheckFailureKind;
774775
module.exports.vitePlusHeader = nativeBinding.vitePlusHeader;

packages/cli/binding/index.d.cts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,5 +369,7 @@ export interface RunCommandResult {
369369
pathAccesses: Record<string, PathAccess>;
370370
}
371371

372+
export declare function takeCheckFailureKind(): string | null;
373+
372374
/** Render the Vite+ header using the Rust implementation. */
373375
export declare function vitePlusHeader(): string;

packages/cli/binding/src/cli.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,13 @@ impl LintMessageKind {
832832
Self::LintAndTypeCheck => "Lint or type issues found",
833833
}
834834
}
835+
836+
fn failure_kind(self) -> &'static str {
837+
match self {
838+
Self::LintOnly => "lint",
839+
Self::LintAndTypeCheck => "type-aware",
840+
}
841+
}
835842
}
836843

837844
fn parse_check_summary(line: &str) -> Option<CheckSummary> {
@@ -1098,6 +1105,7 @@ async fn execute_direct_subcommand(
10981105
"Formatting failed during fix",
10991106
);
11001107
}
1108+
crate::set_last_check_failure_kind(Some("formatting"));
11011109
return Ok(status);
11021110
}
11031111
}
@@ -1175,6 +1183,7 @@ async fn execute_direct_subcommand(
11751183
}
11761184
}
11771185
if status != ExitStatus::SUCCESS {
1186+
crate::set_last_check_failure_kind(Some(lint_message_kind.failure_kind()));
11781187
return Ok(status);
11791188
}
11801189
}
@@ -1211,6 +1220,7 @@ async fn execute_direct_subcommand(
12111220
&combined_output,
12121221
"Formatting failed after lint fixes were applied",
12131222
);
1223+
crate::set_last_check_failure_kind(Some("formatting"));
12141224
return Ok(status);
12151225
}
12161226
if let Some(started) = fmt_fix_started {
@@ -1663,6 +1673,12 @@ mod tests {
16631673
assert_eq!(kind.success_label(), "Found no warnings, lint errors, or type errors");
16641674
assert_eq!(kind.warning_heading(), "Lint or type warnings found");
16651675
assert_eq!(kind.issue_heading(), "Lint or type issues found");
1676+
assert_eq!(kind.failure_kind(), "type-aware");
1677+
}
1678+
1679+
#[test]
1680+
fn lint_message_kind_reports_plain_lint_failures() {
1681+
assert_eq!(LintMessageKind::LintOnly.failure_kind(), "lint");
16661682
}
16671683

16681684
#[test]

packages/cli/binding/src/lib.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ mod package_manager;
1818
#[allow(dead_code)]
1919
mod utils;
2020

21-
use std::{collections::HashMap, error::Error as StdError, ffi::OsStr, fmt::Write as _, sync::Arc};
21+
use std::{
22+
collections::HashMap,
23+
error::Error as StdError,
24+
ffi::OsStr,
25+
fmt::Write as _,
26+
sync::{Arc, LazyLock, Mutex},
27+
};
2228

2329
use napi::{anyhow, bindgen_prelude::*, threadsafe_function::ThreadsafeFunction};
2430
use napi_derive::napi;
@@ -28,6 +34,21 @@ use crate::cli::{
2834
BoxedResolverFn, CliOptions as ViteTaskCliOptions, ResolveCommandResult, ViteConfigResolverFn,
2935
};
3036

37+
static LAST_CHECK_FAILURE_KIND: LazyLock<Mutex<Option<String>>> =
38+
LazyLock::new(|| Mutex::new(None));
39+
40+
pub(crate) fn set_last_check_failure_kind(kind: Option<&str>) {
41+
let mut last_failure_kind =
42+
LAST_CHECK_FAILURE_KIND.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
43+
*last_failure_kind = kind.map(str::to_owned);
44+
}
45+
46+
fn take_last_check_failure_kind() -> Option<String> {
47+
let mut last_failure_kind =
48+
LAST_CHECK_FAILURE_KIND.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
49+
last_failure_kind.take()
50+
}
51+
3152
/// Module initialization - sets up tracing for debugging
3253
#[napi_derive::module_init]
3354
pub fn init() {
@@ -130,6 +151,8 @@ fn format_error_message(error: &(dyn StdError + 'static)) -> String {
130151
/// and process JavaScript callbacks (via ThreadsafeFunction).
131152
#[napi]
132153
pub async fn run(options: CliOptions) -> Result<i32> {
154+
set_last_check_failure_kind(None);
155+
133156
// Use provided cwd or current directory
134157
let mut cwd = current_dir()?;
135158
if let Some(options_cwd) = options.cwd {
@@ -198,6 +221,11 @@ pub async fn run(options: CliOptions) -> Result<i32> {
198221
}
199222
}
200223

224+
#[napi]
225+
pub fn take_check_failure_kind() -> Option<String> {
226+
take_last_check_failure_kind()
227+
}
228+
201229
/// Render the Vite+ header using the Rust implementation.
202230
#[napi]
203231
pub fn vite_plus_header() -> String {

packages/cli/build.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import { execSync } from 'node:child_process';
2222
import { existsSync, globSync, readFileSync, readdirSync, statSync } from 'node:fs';
23-
import { copyFile, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
23+
import { copyFile, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
2424
import { dirname, join } from 'node:path';
2525
import { fileURLToPath } from 'node:url';
2626
import { parseArgs } from 'node:util';
@@ -60,6 +60,7 @@ const napiArgs = process.argv
6060

6161
if (!skipTs) {
6262
await buildCli();
63+
await copyCliAssets();
6364
buildGlobalModules();
6465
generateLicenseFile({
6566
title: 'Vite-Plus CLI license',
@@ -165,10 +166,11 @@ async function buildCli() {
165166
console.error(formatDiagnostics(cjsDiagnostics, cjsHost));
166167
process.exit(1);
167168
}
168-
await rename(
169-
join(projectDir, 'dist/define-config.js'),
170-
join(projectDir, 'dist/define-config.cjs'),
171-
);
169+
const defineConfigJsPath = join(projectDir, 'dist/define-config.js');
170+
const defineConfigCjsPath = join(projectDir, 'dist/define-config.cjs');
171+
if (existsSync(defineConfigJsPath)) {
172+
await copyFile(defineConfigJsPath, defineConfigCjsPath);
173+
}
172174

173175
const host = createCompilerHost(options);
174176

@@ -196,6 +198,30 @@ async function buildCli() {
196198
console.error(formatDiagnostics(diagnostics, host));
197199
process.exit(1);
198200
}
201+
202+
if (existsSync(defineConfigJsPath)) {
203+
await copyFile(defineConfigJsPath, defineConfigCjsPath);
204+
} else if (existsSync(defineConfigCjsPath)) {
205+
await copyFile(defineConfigCjsPath, defineConfigJsPath);
206+
}
207+
}
208+
209+
async function copyCliAssets() {
210+
const assetsDir = join(projectDir, 'assets');
211+
if (!existsSync(assetsDir)) {
212+
return;
213+
}
214+
215+
const distAssetsDir = join(projectDir, 'dist', 'assets');
216+
await mkdir(distAssetsDir, { recursive: true });
217+
218+
for (const entry of readdirSync(assetsDir)) {
219+
const sourcePath = join(assetsDir, entry);
220+
if (!statSync(sourcePath).isFile()) {
221+
continue;
222+
}
223+
await copyFile(sourcePath, join(distAssetsDir, entry));
224+
}
199225
}
200226

201227
function buildGlobalModules() {

packages/cli/package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@
102102
"types": "./dist/test/internal/browser.d.ts",
103103
"default": "./dist/test/internal/browser.js"
104104
},
105+
"./test/internal/module-runner": {
106+
"types": "./dist/test/internal/module-runner.d.ts",
107+
"default": "./dist/test/internal/module-runner.js"
108+
},
105109
"./test/runners": {
106110
"types": "./dist/test/runners.d.ts",
107111
"default": "./dist/test/runners.js"
@@ -131,9 +135,9 @@
131135
"types": "./dist/test/snapshot.d.ts",
132136
"default": "./dist/test/snapshot.js"
133137
},
134-
"./test/runtime": {
135-
"types": "./dist/test/runtime.d.ts",
136-
"default": "./dist/test/runtime.js"
138+
"./test/mocker": {
139+
"types": "./dist/test/mocker.d.ts",
140+
"default": "./dist/test/mocker.js"
137141
},
138142
"./test/worker": {
139143
"types": "./dist/test/worker.d.ts",

packages/cli/src/bin.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import path from 'node:path';
1414

15-
import { run } from '../binding/index.js';
15+
import { detectWorkspace, run, takeCheckFailureKind } from '../binding/index.js';
1616
import { applyToolInitConfigToViteConfig, inspectInitCommand } from './init-config.js';
1717
import { doc } from './resolve-doc.js';
1818
import { fmt } from './resolve-fmt.js';
@@ -21,6 +21,12 @@ import { pack } from './resolve-pack.js';
2121
import { test } from './resolve-test.js';
2222
import { resolveUniversalViteConfig } from './resolve-vite-config.js';
2323
import { vite } from './resolve-vite.js';
24+
import { recordCheckFailureAchievement } from './utils/check-achievements.js';
25+
import {
26+
normalizeCheckFailureKind,
27+
printCheckFailureImage,
28+
type CheckFailureKind,
29+
} from './utils/check-failure-image.js';
2430
import { accent, errorMsg, log } from './utils/terminal.js';
2531

2632
function getErrorMessage(err: unknown): string {
@@ -35,6 +41,22 @@ function getErrorMessage(err: unknown): string {
3541
return String(err);
3642
}
3743

44+
async function celebrateCheckFailure(failureKind: CheckFailureKind) {
45+
let projectRoot = process.cwd();
46+
47+
try {
48+
projectRoot = (await detectWorkspace(projectRoot)).root ?? projectRoot;
49+
} catch {
50+
// Fall back to the current working directory if workspace detection fails.
51+
}
52+
53+
const achievementUpdate = recordCheckFailureAchievement(projectRoot, failureKind);
54+
await printCheckFailureImage(process.stderr, process.env, {
55+
achievementUpdate,
56+
failureKind,
57+
});
58+
}
59+
3860
// Parse command line arguments
3961
let args = process.argv.slice(2);
4062

@@ -94,6 +116,9 @@ if (command === 'create') {
94116
});
95117

96118
let finalExitCode = exitCode;
119+
if (command === 'check' && finalExitCode !== 0) {
120+
await celebrateCheckFailure(normalizeCheckFailureKind(takeCheckFailureKind()));
121+
}
97122
if (exitCode === 0) {
98123
try {
99124
const result = await applyToolInitConfigToViteConfig(command, args.slice(1));
@@ -126,6 +151,9 @@ if (command === 'create') {
126151
process.exit(finalExitCode);
127152
} catch (err) {
128153
errorMsg(getErrorMessage(err));
154+
if (command === 'check') {
155+
await celebrateCheckFailure(normalizeCheckFailureKind(takeCheckFailureKind()));
156+
}
129157
process.exit(1);
130158
}
131159
}

0 commit comments

Comments
 (0)