@weapp-vite/oxfmt 0.0.0-stage → 0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 ice breaker
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 CHANGED
@@ -1,3 +1,72 @@
1
- # Temporary Holding Version
1
+ # @weapp-vite/oxfmt
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 小程序共享格式预设与安全格式化入口。配套 Vite+ 1.2.0 / Oxfmt 0.73.0,CLI 与 VSCode 共用 `@weapp-vite/language-service`。
4
+
5
+ ```ts
6
+ import { recommended } from '@weapp-vite/oxfmt'
7
+ import { defineConfig } from 'vite-plus'
8
+
9
+ export default defineConfig({
10
+ fmt: recommended,
11
+ })
12
+ ```
13
+
14
+ 默认两空格、单引号、无分号、LF、末尾换行和多行尾逗号。不排序 import、`package.json` 字段或 Tailwind class。
15
+
16
+ ## 项目命令
17
+
18
+ ```sh
19
+ pnpm exec weapp-vite-format --write
20
+ pnpm exec weapp-vite-format --check
21
+ pnpm exec weapp-vite-format --check-all
22
+ ```
23
+
24
+ | 命令 | 处理范围 |
25
+ | --- | --- |
26
+ | `--write` | 在内存完成全部候选文件的格式与语义预检,通过后写入 |
27
+ | `--check` | 使用相同格式入口只读检查,任何文件需要格式化时失败 |
28
+ | `--check-all` | 一次 `vp check`、原生语言预检与格式检查,以及项目存在的 `lint:vue`、`stylelint` 脚本 |
29
+
30
+ 标准文件使用项目安装的 Oxfmt API,原生模板、脚本模块和样式进入对应适配器。`--check-all` 不替代项目独立的 TypeScript 类型检查。原生语言诊断也可通过 `weapp-vite-lint --native-only` 单独执行。
31
+
32
+ ## 语言范围与语义保护
33
+
34
+ | 文件 | 处理方式 |
35
+ | --- | --- |
36
+ | `.wxml`、`.axml`、`.ttml`、`.swan`、`.jxml`、`.qml`、`.ksml`、`.xhsml`、`.tyml` | 共享 TS scanner 与源码片段编辑,仅调整标签和属性布局 |
37
+ | `.wxss`、`.acss`、`.ttss`、`.qss`、`.jxss`、`.tyss` 和原生 `.css` | CSS 格式化,保留 `rpx`、Tailwind 指令、条件注释和声明顺序;Stylelint 负责样式语义 |
38
+ | 独立/内联 WXS、SJS、KS、filter | 平台方言预检、词法兼容处理、Oxfmt、格式化后 AST 与方言复验 |
39
+ | `.wxs.ts`、`.sjs.ts`、`.ks.ts`、`.filter.ts` 及 `lang="ts/js"` | 按待编译源码处理,不把合法源码提前限制为宿主原生语法 |
40
+
41
+ 模板逐字保留文本空白、实体、插值、属性值及引号、属性顺序、重复属性和显式闭合形式,不经过 DOM 序列化。内联脚本由模块适配器独立处理;`formatWxml` 兼容 API 仍保持内联脚本原文。
42
+
43
+ 脚本模块按实际平台检查模块系统和内建能力,不把所有 `.sjs` 解释成微信 WXS。百度原生 filter 使用相对 `.filter.js` 引用并通过默认导出暴露函数。方言、词法、结构或 AST 语义校验失败时拒绝写回;文件在预检期间被修改时拒绝覆盖。
44
+
45
+ 预设排除依赖、构建产物、`.weapp-vite`、受管生成目录与锁文件。供标准 `vp fmt` 跳过原生模板和模块的忽略项由组合 CLI 接管,不会因此跳过普通源码。不要把原生模板重新关联为 HTML;单独的 `vp fmt` 不包含完整原生语言检查。
46
+
47
+ ## 配置与 workspace
48
+
49
+ `lint` / `fmt` 继续位于 `vite.config.ts`。平台选择放在 `weapp.language`,支持 `platform` 与 `overrides: [{ files: string[], platform }]`;配置影响 CLI 和 VSCode。存在歧义或互相冲突的模块引用时给出诊断,需显式配置归属。
50
+
51
+ `--write` / `--check` 使用最近的项目配置,也可通过 `--config` 指定 Vite 配置或 `.oxfmtrc.json`。格式遵循实际 `fmt`、文件 `overrides`、忽略路径和 `.gitignore`;不通过忽略普通源码消除格式失败。
52
+
53
+ ```sh
54
+ pnpm exec weapp-vite-format --write src/pages
55
+ pnpm exec weapp-vite-format --check -c .oxfmtrc.json 'src/**/*.wxml'
56
+ ```
57
+
58
+ `--check-all` 与 `vp check` 优先使用 workspace 根配置,不接受 `--config`;成员预设不覆盖父工作区的 `lint` / `fmt` 所有权。根配置的 glob 和忽略路径需相对根目录定位到成员项目。
59
+
60
+ ## 程序接口与编辑器
61
+
62
+ ```ts
63
+ import { formatDocument } from '@weapp-vite/oxfmt/languages'
64
+ import { formatWxml } from '@weapp-vite/oxfmt/wxml'
65
+
66
+ const template = await formatDocument(source, 'src/page.axml')
67
+ const compatible = await formatWxml(source, 'src/page.wxml', { printWidth: 100 })
68
+ ```
69
+
70
+ `formatDocument` 支持全部登记语言与内联模块。`@weapp-vite/oxfmt/project` 导出项目配置解析、文档归属解析和 `prepareProjectFormatting`,供 CLI 与编辑器适配层复用。显式传入格式选项时采用该配置;需要固定风格时提供完整所需选项。
71
+
72
+ VSCode 扩展使用项目安装的工具,仅在受信任工作区加载项目配置与格式化器。模板及原生模块使用扩展的唯一格式化提供者,标准 JS/TS 使用 Oxc;错误输入显示诊断并返回零修改。此包不新增完整模板类型检查器,不替代 Vue 专项 ESLint、Stylelint 或平台编译器。本轮验收覆盖 TS、CLI、VSCode 和构建,不代表真实小程序 IDE runtime 验收。
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ export {}
package/dist/cli.mjs ADDED
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+ import { recommended } from "./index.mjs";
3
+ import { t as prepareFormatting } from "./prepare-D65ZFJWl.mjs";
4
+ import process from "node:process";
5
+ import * as fs from "node:fs/promises";
6
+ import { readFile } from "node:fs/promises";
7
+ import path from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+ import { loadProjectConfig } from "@weapp-vite/language-service/project";
10
+ import { execa } from "execa";
11
+ // src/project/args.ts
12
+ /** CLI 参数只描述检查对象和模式,不能静默接受未实现的开关。 */
13
+ function parseArgs(args) {
14
+ let mode;
15
+ let configFile;
16
+ const inputs = [];
17
+ for (let index = 0; index < args.length; index++) {
18
+ const argument = args[index];
19
+ if (argument === "--write" || argument === "--check" || argument === "--check-all") {
20
+ if (mode && mode !== argument) throw new Error("只能选择一种格式检查或写入模式");
21
+ mode = argument;
22
+ } else if (argument === "-c" || argument === "--config") {
23
+ configFile = args[++index];
24
+ if (!configFile || configFile.startsWith("-")) throw new Error("--config 需要配置文件路径");
25
+ } else if (argument.startsWith("--config=")) {
26
+ configFile = argument.slice(9);
27
+ if (!configFile) throw new Error("--config 需要配置文件路径");
28
+ } else if (argument.startsWith("-")) throw new Error(`未知参数:${argument}`);
29
+ else inputs.push(argument);
30
+ }
31
+ if (!mode) throw new Error("请指定 --write、--check 或 --check-all");
32
+ if (mode === "--check-all" && configFile) throw new Error("Vite+ check 使用工作区配置,不接受 --config");
33
+ return {
34
+ mode,
35
+ configFile,
36
+ inputs: inputs.length ? inputs : ["."]
37
+ };
38
+ }
39
+ // src/project/transaction.ts
40
+ /** 同卷暂存新旧完整内容,所有暂存成功后才原子替换;回滚只触及仍归本次操作的内容。 */
41
+ async function commitFormatting(candidates, io = fs) {
42
+ const staged = [];
43
+ const written = [];
44
+ const failures = [];
45
+ try {
46
+ for (const candidate of candidates.filter((item) => item.source !== item.formatted)) {
47
+ const directory = await io.mkdtemp(path.join(path.dirname(candidate.file), ".weapp-vite-format-"));
48
+ const entry = {
49
+ candidate,
50
+ directory,
51
+ next: path.join(directory, "next"),
52
+ previous: path.join(directory, "previous")
53
+ };
54
+ staged.push(entry);
55
+ const { mode } = await io.stat(candidate.file);
56
+ await io.writeFile(entry.previous, candidate.source, { mode });
57
+ await io.writeFile(entry.next, candidate.formatted, { mode });
58
+ }
59
+ for (const candidate of candidates) if (await io.readFile(candidate.file, "utf8") !== candidate.source) throw new Error("格式化期间项目文件发生变化,拒绝覆盖");
60
+ for (const entry of staged) {
61
+ if (await io.readFile(entry.candidate.file, "utf8") !== entry.candidate.source) throw new Error("格式化期间项目文件发生变化,拒绝覆盖");
62
+ await io.rename(entry.next, entry.candidate.file);
63
+ written.push(entry);
64
+ }
65
+ } catch (error) {
66
+ failures.push(error);
67
+ for (const entry of written.reverse()) try {
68
+ if (await io.readFile(entry.candidate.file, "utf8") === entry.candidate.formatted) await io.rename(entry.previous, entry.candidate.file);
69
+ } catch (restoreError) {
70
+ failures.push(restoreError);
71
+ }
72
+ } finally {
73
+ for (const entry of staged) try {
74
+ await io.rm(entry.directory, {
75
+ recursive: true,
76
+ force: true
77
+ });
78
+ } catch (cleanupError) {
79
+ failures.push(cleanupError);
80
+ }
81
+ }
82
+ if (failures.length) throw new AggregateError(failures, "格式化写入失败,已回滚仍归本次写入的文件");
83
+ }
84
+ // src/project/index.ts
85
+ async function checkProject(cwd, inputs) {
86
+ const vp = fileURLToPath(new URL("../bin/vp", import.meta.resolve("vite-plus")));
87
+ let exitCode = (await execa(process.execPath, [
88
+ vp,
89
+ "check",
90
+ ...inputs
91
+ ], {
92
+ cwd,
93
+ stdio: "inherit",
94
+ reject: false
95
+ })).exitCode ?? 1;
96
+ const manifest = JSON.parse(await readFile(path.join(cwd, "package.json"), "utf8"));
97
+ for (const script of ["lint:vue", "stylelint"]) if (manifest.scripts?.[script]) {
98
+ const checked = await execa("npm", ["run", script], {
99
+ cwd,
100
+ stdio: "inherit",
101
+ reject: false
102
+ });
103
+ exitCode ||= checked.exitCode ?? 1;
104
+ }
105
+ return exitCode;
106
+ }
107
+ /** 一次预检全部候选文件;读取后发生源码变化时拒绝覆盖,检查模式不写入。 */
108
+ async function runFormat(args, cwd = process.cwd()) {
109
+ if (args.includes("--help") || args.includes("-h")) {
110
+ process.stdout.write("weapp-vite-format <--write|--check|--check-all> [-c config.json] [paths...]\n");
111
+ return;
112
+ }
113
+ const { mode, configFile, inputs } = parseArgs(args);
114
+ const project = await loadProjectConfig(cwd, {
115
+ configFile,
116
+ workspaceFirst: mode === "--check-all"
117
+ });
118
+ const scopedInputs = inputs.map((input) => {
119
+ const negative = input.startsWith("!");
120
+ const target = negative ? input.slice(1) : input;
121
+ const relative = path.relative(project.root, path.resolve(cwd, target)).split(path.sep).join("/");
122
+ return `${negative ? "!" : ""}${relative || "."}`;
123
+ });
124
+ const candidates = await prepareFormatting(project.root, scopedInputs, project.fmt ?? recommended, project.language);
125
+ const changes = candidates.filter((candidate) => candidate.source !== candidate.formatted);
126
+ if (mode !== "--write") {
127
+ for (const change of changes) {
128
+ const label = path.relative(cwd, change.file).split(path.sep).join("/");
129
+ process.stderr.write(`${change.file.endsWith(".wxml") ? "WXML" : "文件"} 需要格式化:${label}\n`);
130
+ }
131
+ const exitCode = mode === "--check-all" ? await checkProject(cwd, inputs) : 0;
132
+ if (exitCode !== 0 || changes.length > 0) process.exitCode = exitCode || 1;
133
+ return;
134
+ }
135
+ await commitFormatting(candidates);
136
+ }
137
+ // src/cli.ts
138
+ runFormat(process.argv.slice(2)).catch((error) => {
139
+ console.error(error instanceof Error ? error.message : String(error));
140
+ process.exitCode = 1;
141
+ });
142
+ export {};
@@ -0,0 +1,20 @@
1
+ export declare const nativeFormatIgnorePatterns: string[];
2
+ /** 小程序格式预设;原生模板和模块交给安全适配器,样式语义仍交给 Stylelint。 */
3
+ export declare const recommended: {
4
+ ignorePatterns: string[];
5
+ associations: {
6
+ files: string[];
7
+ language: "css";
8
+ }[];
9
+ tabWidth: number;
10
+ useTabs: false;
11
+ singleQuote: true;
12
+ semi: false;
13
+ trailingComma: "all";
14
+ endOfLine: "lf";
15
+ insertFinalNewline: true;
16
+ htmlWhitespaceSensitivity: "strict";
17
+ sortImports: false;
18
+ sortPackageJson: false;
19
+ sortTailwindcss: false;
20
+ };
package/dist/index.mjs ADDED
@@ -0,0 +1,33 @@
1
+ import { t as formatOptions } from "./options-CitIeLO-.mjs";
2
+ import { FILTER_EXTENSIONS, SCRIPT_EXTENSIONS, STYLE_EXTENSIONS, TEMPLATE_EXTENSIONS } from "@weapp-vite/language-service";
3
+ // src/index.ts
4
+ const nativeFormatIgnorePatterns = [
5
+ ...FILTER_EXTENSIONS.map((extension) => `**/*${extension}`),
6
+ ...TEMPLATE_EXTENSIONS.map((extension) => `**/*${extension}`),
7
+ ...SCRIPT_EXTENSIONS.flatMap((extension) => [
8
+ `**/*${extension}`,
9
+ `**/*${extension}.ts`,
10
+ `**/*${extension}.js`
11
+ ])
12
+ ];
13
+ /** 小程序格式预设;原生模板和模块交给安全适配器,样式语义仍交给 Stylelint。 */
14
+ const recommended = {
15
+ ...formatOptions,
16
+ ignorePatterns: [
17
+ "**/node_modules/**",
18
+ "**/dist/**",
19
+ "**/dist-*/**",
20
+ "**/.weapp-vite/**",
21
+ "**/miniprogram_npm/**",
22
+ "**/generated/**",
23
+ "**/pnpm-lock.yaml",
24
+ "**/package-lock.json",
25
+ "**/yarn.lock",
26
+ ...nativeFormatIgnorePatterns
27
+ ],
28
+ associations: [{
29
+ files: STYLE_EXTENSIONS.map((extension) => `**/*${extension}`),
30
+ language: "css"
31
+ }]
32
+ };
33
+ export { nativeFormatIgnorePatterns, recommended };
@@ -0,0 +1,5 @@
1
+ import { DocumentInput, LanguageOptions } from "@weapp-vite/language-service";
2
+ import { FormatConfig } from "vite-plus/fmt";
3
+ export type DocumentFormatOptions = FormatConfig & LanguageOptions & Pick<DocumentInput, 'sourceKind' | 'dialect' | 'language'>;
4
+ /** 同一公开入口供 CLI 与编辑器格式化模板、内联/独立模块及原生样式。 */
5
+ export declare function formatDocument(source: string, fileName: string, options?: DocumentFormatOptions): Promise<string>;
@@ -0,0 +1,62 @@
1
+ import { t as formatOptions } from "../options-CitIeLO-.mjs";
2
+ import { LanguageError, STYLE_EXTENSIONS, TEMPLATE_EXTENSIONS, analyzeDocument, formatScript, formatTemplate, getTemplateScriptInput, isScriptModuleFile, resolveLanguageProfile } from "@weapp-vite/language-service";
3
+ // src/languages/index.ts
4
+ /** 同一公开入口供 CLI 与编辑器格式化模板、内联/独立模块及原生样式。 */
5
+ async function formatDocument(source, fileName, options = formatOptions) {
6
+ const profile = resolveLanguageProfile(fileName, options);
7
+ const normalized = fileName.toLowerCase();
8
+ const settings = { ...options };
9
+ if (TEMPLATE_EXTENSIONS.some((extension) => normalized.endsWith(extension))) {
10
+ const analysis = analyzeDocument({
11
+ source,
12
+ fileName,
13
+ options
14
+ });
15
+ if (!analysis.valid) throw new LanguageError(fileName, analysis.diagnostics.filter((diagnostic) => diagnostic.severity === "error"));
16
+ let formatted = source;
17
+ for (const script of [...analysis.template?.scripts ?? []].sort((left, right) => right.start - left.start)) {
18
+ const input = getTemplateScriptInput(fileName, script, analysis.platform);
19
+ const result = await formatScript(input.source, input.fileName, {
20
+ platform: input.platform,
21
+ sourceKind: input.sourceKind,
22
+ language: input.language,
23
+ dialect: input.dialect,
24
+ format: async (virtualFile, code, required) => {
25
+ const { format } = await import("vite-plus/fmt");
26
+ return format(virtualFile, code, {
27
+ ...settings,
28
+ ...required
29
+ });
30
+ }
31
+ });
32
+ if (result.diagnostics.some((diagnostic) => diagnostic.severity === "error")) throw new LanguageError(fileName, result.diagnostics.map((diagnostic) => ({
33
+ ...diagnostic,
34
+ start: diagnostic.start + script.start,
35
+ end: diagnostic.end + script.start
36
+ })));
37
+ formatted = formatted.slice(0, script.start) + result.code + formatted.slice(script.end);
38
+ }
39
+ return formatTemplate(formatted, fileName, settings);
40
+ }
41
+ const { format } = await import("vite-plus/fmt").catch((cause) => {
42
+ throw new Error("脚本与样式格式化需要项目安装 Vite+ >=1.2.0。", { cause });
43
+ });
44
+ if (isScriptModuleFile(normalized) || options.dialect) {
45
+ const result = await formatScript(source, fileName, {
46
+ platform: profile?.platform,
47
+ sourceKind: options.sourceKind,
48
+ dialect: options.dialect,
49
+ language: options.language,
50
+ format: (virtualFile, code, required) => format(virtualFile, code, {
51
+ ...settings,
52
+ ...required
53
+ })
54
+ });
55
+ if (result.diagnostics.some((diagnostic) => diagnostic.severity === "error")) throw new LanguageError(fileName, result.diagnostics);
56
+ return result.code;
57
+ }
58
+ const result = await format(STYLE_EXTENSIONS.some((extension) => normalized.endsWith(extension)) ? `${fileName}.css` : fileName, source, settings);
59
+ if (result.errors.length > 0) throw new Error(`${fileName}: ${result.errors.map((error) => error.message).join("\n")}`);
60
+ return result.code;
61
+ }
62
+ export { formatDocument };
@@ -0,0 +1,15 @@
1
+ // src/options.ts
2
+ const formatOptions = {
3
+ tabWidth: 2,
4
+ useTabs: false,
5
+ singleQuote: true,
6
+ semi: false,
7
+ trailingComma: "all",
8
+ endOfLine: "lf",
9
+ insertFinalNewline: true,
10
+ htmlWhitespaceSensitivity: "strict",
11
+ sortImports: false,
12
+ sortPackageJson: false,
13
+ sortTailwindcss: false
14
+ };
15
+ export { formatOptions as t };
@@ -0,0 +1,88 @@
1
+ import { nativeFormatIgnorePatterns, recommended } from "./index.mjs";
2
+ import { formatDocument } from "./languages/index.mjs";
3
+ import { STYLE_EXTENSIONS } from "@weapp-vite/language-service";
4
+ import { readFile } from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { analyzeProject, collectProjectFiles, isNativeDocument } from "@weapp-vite/language-service/project";
7
+ import picomatch from "picomatch";
8
+ // src/project/config.ts
9
+ const LANGUAGE_EXTENSION = {
10
+ "javascript": "js",
11
+ "jsx": "jsx",
12
+ "typescript": "ts",
13
+ "tsx": "tsx",
14
+ "json": "json",
15
+ "jsonc": "jsonc",
16
+ "json5": "json5",
17
+ "css": "css",
18
+ "scss": "scss",
19
+ "less": "less",
20
+ "graphql": "graphql",
21
+ "yaml": "yaml",
22
+ "markdown": "md",
23
+ "toml": "toml",
24
+ "html": "html",
25
+ "angular-html": "component.html",
26
+ "vue": "vue",
27
+ "svelte": "svelte",
28
+ "astro": "astro",
29
+ "handlebars": "hbs",
30
+ "mdx": "mdx"
31
+ };
32
+ /** 文件收集和关联由项目层处理,格式化引擎只接收当前文件的选项。 */
33
+ function toFormatOptions(config) {
34
+ const { associations: _associations, ignorePatterns: _ignorePatterns, overrides: _overrides, ...options } = config;
35
+ return options;
36
+ }
37
+ /** CLI 与编辑器对项目相对路径应用同一份文件覆盖及语言关联。 */
38
+ function resolveDocumentFormatOptions(fileName, config) {
39
+ fileName = fileName.replaceAll("\\", "/").replace(/^\.\//, "");
40
+ const matches = (files, excludeFiles = []) => picomatch(files, { dot: true })(fileName) && !picomatch(excludeFiles, { dot: true })(fileName);
41
+ let options = toFormatOptions(config);
42
+ for (const override of config.overrides ?? []) if (matches(override.files, override.excludeFiles)) options = {
43
+ ...options,
44
+ ...override.options
45
+ };
46
+ let virtualFile = fileName;
47
+ if (!isNativeDocument(fileName) && !STYLE_EXTENSIONS.some((extension) => fileName.toLowerCase().endsWith(extension))) {
48
+ for (const association of config.associations ?? []) if (matches(association.files, association.excludeFiles)) virtualFile = `${fileName}.${LANGUAGE_EXTENSION[association.language]}`;
49
+ }
50
+ return {
51
+ fileName: virtualFile,
52
+ options
53
+ };
54
+ }
55
+ // src/project/prepare.ts
56
+ const STANDARD_FILE = /\.(?:[cm]?[jt]sx?|json[5c]?|css|scss|less|html?|vue|ya?ml|md|markdown|mdx|graphql|gql|toml|svelte|astro|hbs|handlebars)$/i;
57
+ /** 所有文件先在内存完成解析、方言和格式校验,再交给调用方一次提交。 */
58
+ async function prepareFormatting(root, inputs, config, language) {
59
+ const ignores = [...recommended.ignorePatterns, ...config.ignorePatterns ?? []].filter((pattern) => !nativeFormatIgnorePatterns.includes(pattern));
60
+ const files = await collectProjectFiles(root, inputs, ignores);
61
+ const native = new Map((await analyzeProject(root, inputs, language, ignores)).map((document) => [document.file, document]));
62
+ const candidates = [];
63
+ for (const file of files) {
64
+ const fileName = path.relative(root, file).split(path.sep).join("/");
65
+ const settings = resolveDocumentFormatOptions(fileName, config);
66
+ if (!isNativeDocument(file) && !STANDARD_FILE.test(file) && !STYLE_EXTENSIONS.some((extension) => file.endsWith(extension)) && settings.fileName === fileName) continue;
67
+ const source = native.get(file)?.source ?? await readFile(file, "utf8");
68
+ const document = native.get(file);
69
+ if (document && !document.analysis.valid) throw new Error(document.analysis.diagnostics.filter((diagnostic) => diagnostic.severity === "error").map((diagnostic) => `${fileName}:${diagnostic.start + 1}: ${diagnostic.message}`).join("\n"));
70
+ const formatted = await formatDocument(source, settings.fileName, {
71
+ ...settings.options,
72
+ ...language,
73
+ ...document ? {
74
+ sourceKind: document.input.sourceKind,
75
+ dialect: document.input.dialect,
76
+ language: document.input.language
77
+ } : {},
78
+ ...document?.platform ? { platform: document.platform } : {}
79
+ });
80
+ candidates.push({
81
+ file,
82
+ source,
83
+ formatted
84
+ });
85
+ }
86
+ return candidates;
87
+ }
88
+ export { resolveDocumentFormatOptions as n, prepareFormatting as t };
@@ -0,0 +1,16 @@
1
+ import { LanguageOptions } from "@weapp-vite/language-service";
2
+ import { ProjectLanguageConfig, loadProjectConfig, resolveProjectDocumentInput, resolveProjectReference } from "@weapp-vite/language-service/project";
3
+ import { FormatConfig, OxfmtConfig } from "vite-plus/fmt";
4
+ /** CLI 与编辑器对项目相对路径应用同一份文件覆盖及语言关联。 */
5
+ export declare function resolveDocumentFormatOptions(fileName: string, config: OxfmtConfig): {
6
+ fileName: string;
7
+ options: FormatConfig;
8
+ };
9
+ interface FormatCandidate {
10
+ file: string;
11
+ source: string;
12
+ formatted: string;
13
+ }
14
+ /** 所有文件先在内存完成解析、方言和格式校验,再交给调用方一次提交。 */
15
+ declare function prepareFormatting(root: string, inputs: string[], config: OxfmtConfig, language: LanguageOptions): Promise<FormatCandidate[]>;
16
+ export { type FormatCandidate, type ProjectLanguageConfig, loadProjectConfig, prepareFormatting as prepareProjectFormatting, resolveProjectDocumentInput, resolveProjectReference };
@@ -0,0 +1,3 @@
1
+ import { n as resolveDocumentFormatOptions, t as prepareFormatting } from "../prepare-D65ZFJWl.mjs";
2
+ import { loadProjectConfig, resolveProjectDocumentInput, resolveProjectReference } from "@weapp-vite/language-service/project";
3
+ export { loadProjectConfig, prepareFormatting as prepareProjectFormatting, resolveDocumentFormatOptions, resolveProjectDocumentInput, resolveProjectReference };
@@ -0,0 +1,3 @@
1
+ import { FormatConfig } from "vite-plus/fmt";
2
+ /** 兼容原有 API:只重排标签,保留文本、表达式、属性原文和内联脚本。 */
3
+ export declare function formatWxml(source: string, fileName?: string, options?: FormatConfig): Promise<string>;
@@ -0,0 +1,11 @@
1
+ import { t as formatOptions } from "../options-CitIeLO-.mjs";
2
+ import { formatTemplate } from "@weapp-vite/language-service";
3
+ // src/wxml/index.ts
4
+ /** 兼容原有 API:只重排标签,保留文本、表达式、属性原文和内联脚本。 */
5
+ async function formatWxml(source, fileName = "component.wxml", options = {}) {
6
+ return formatTemplate(source, fileName, {
7
+ ...formatOptions,
8
+ ...options
9
+ });
10
+ }
11
+ export { formatWxml };
package/package.json CHANGED
@@ -1,6 +1,96 @@
1
1
  {
2
2
  "name": "@weapp-vite/oxfmt",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "type": "module",
4
+ "version": "0.0.0",
5
+ "description": "Oxfmt formatting preset for weapp-vite mini-program projects",
6
+ "license": "MIT",
7
+ "homepage": "https://vite.weapp.dev/packages/oxfmt",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/weapp-vite/weapp-vite.git",
11
+ "directory": "packages/oxfmt"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/weapp-vite/weapp-vite/issues"
15
+ },
16
+ "keywords": [
17
+ "weapp-vite",
18
+ "miniprogram",
19
+ "oxfmt",
20
+ "formatter"
21
+ ],
22
+ "sideEffects": false,
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.mts",
26
+ "import": "./dist/index.mjs"
27
+ },
28
+ "./wxml": {
29
+ "types": "./dist/wxml/index.d.mts",
30
+ "import": "./dist/wxml/index.mjs"
31
+ },
32
+ "./languages": {
33
+ "types": "./dist/languages/index.d.mts",
34
+ "import": "./dist/languages/index.mjs"
35
+ },
36
+ "./project": {
37
+ "types": "./dist/project/public.d.mts",
38
+ "import": "./dist/project/public.mjs"
39
+ },
40
+ "./package.json": "./package.json"
41
+ },
42
+ "main": "./dist/index.mjs",
43
+ "module": "./dist/index.mjs",
44
+ "types": "./dist/index.d.mts",
45
+ "bin": {
46
+ "weapp-vite-format": "./dist/cli.mjs"
47
+ },
48
+ "files": [
49
+ "dist"
50
+ ],
51
+ "engines": {
52
+ "node": "^22.23.4 || ^24.22.1 || >=26.12.1"
53
+ },
54
+ "peerDependencies": {
55
+ "vite-plus": ">=1.2.0 <2"
56
+ },
57
+ "peerDependenciesMeta": {
58
+ "vite-plus": {
59
+ "optional": true
60
+ }
61
+ },
62
+ "dependencies": {
63
+ "@weapp-vite/language-service": "0.0.0",
64
+ "execa": "^10.2.1",
65
+ "picomatch": "^4.0.7"
66
+ },
67
+ "devDependencies": {
68
+ "@types/picomatch": "^4.0.3",
69
+ "postcss": "^8.5.32",
70
+ "postcss-value-parser": "4.2.0",
71
+ "typescript": "^6.0.3",
72
+ "vite-plus": "1.2.0",
73
+ "vitest": "^5.0.5"
74
+ },
75
+ "tsd": {
76
+ "compilerOptions": {
77
+ "module": "NodeNext",
78
+ "moduleResolution": "NodeNext",
79
+ "strict": true,
80
+ "skipLibCheck": false
81
+ }
82
+ },
83
+ "publishConfig": {
84
+ "access": "public",
85
+ "registry": "https://registry.npmjs.org"
86
+ },
87
+ "scripts": {
88
+ "build": "tsdown",
89
+ "test": "vitest run --config ./vitest.config.ts",
90
+ "test:types": "pnpm build && pnpm run test:types:check",
91
+ "test:types:check": "tsd --files \"test-d/**/*.test-d.ts\"",
92
+ "typecheck": "tsc -p tsconfig.json --noEmit",
93
+ "lint": "eslint .",
94
+ "lint:fix": "eslint . --fix"
95
+ }
6
96
  }