snapshot of https://github.com/dream-num/univer.git (dev, 2026-09-16; blobs > 131072B omitted)
Some checks failed
📦 Build / build (push) Has been cancelled
🧪 CI Test / test shard (1/4) (push) Has been cancelled
🧪 CI Test / test shard (2/4) (push) Has been cancelled
🧪 CI Test / test shard (3/4) (push) Has been cancelled
🧪 CI Test / test shard (4/4) (push) Has been cancelled
🧪 CI Test / test (push) Has been cancelled
🧪 CI Test / eslint (push) Has been cancelled
🧪 CI Test / typecheck (push) Has been cancelled
🔫 Sync to Pro / dispatch-sync (push) Has been cancelled
🔮 Sync Mirror / sync (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-16 05:09:45 +05:00
commit bcf6ef63ad
7118 changed files with 1236854 additions and 0 deletions

View File

@@ -0,0 +1,133 @@
import { describe, expect, it } from 'vitest';
import { cleanupClassNameTemplateWhitespace } from './class-name-whitespace-cleanup';
describe('cleanupClassNameTemplateWhitespace', () => {
it('should normalize whitespace for clsx template arguments', () => {
const sourceCode = `
const value = clsx(\`
univer-flex univer-size-6 univer-cursor-pointer univer-items-center univer-justify-center
univer-justify-self-center univer-rounded
hover:univer-bg-gray-100
dark:hover:!univer-bg-gray-700
\`, {
active: true,
});
`;
expect(cleanupClassNameTemplateWhitespace(sourceCode, '/tmp/example.tsx')).toContain(
'clsx("univer-flex univer-size-6 univer-cursor-pointer univer-items-center univer-justify-center univer-justify-self-center univer-rounded hover:univer-bg-gray-100 dark:hover:!univer-bg-gray-700", {'
);
});
it('should normalize whitespace for conditional clsx template arguments', () => {
const sourceCode = `
const value = clsx(
"univer-relative univer-transition-all univer-duration-150",
isDraggingItem && "univer-opacity-0",
dragOverId === itemId && !isDraggingItem && \`
univer-bg-primary-50/60
dark:!univer-bg-primary-900/20
univer-rounded univer-border univer-border-primary-200
dark:!univer-border-primary-700
\`
);
`;
expect(cleanupClassNameTemplateWhitespace(sourceCode, '/tmp/example.tsx')).toContain(
'dragOverId === itemId && !isDraggingItem && "univer-bg-primary-50/60 dark:!univer-bg-primary-900/20 univer-rounded univer-border univer-border-primary-200 dark:!univer-border-primary-700"'
);
});
it('should normalize whitespace for className template literals', () => {
const sourceCode = `
const value = (
<div
className={\`
univer-box-border univer-grid univer-grid-cols-5 univer-gap-2 univer-text-gray-600
dark:!univer-text-gray-200
\`}
/>
);
`;
expect(cleanupClassNameTemplateWhitespace(sourceCode, '/tmp/example.tsx')).toContain(
'className={"univer-box-border univer-grid univer-grid-cols-5 univer-gap-2 univer-text-gray-600 dark:!univer-text-gray-200"}'
);
});
it('should normalize whitespace for className string literals', () => {
const sourceCode = `
const value = (
<button
className="
univer-flex univer-cursor-pointer univer-items-center univer-justify-center
univer-border-none
hover:univer-opacity-70
"
/>
);
`;
expect(cleanupClassNameTemplateWhitespace(sourceCode, '/tmp/example.tsx')).toContain(
'className="univer-flex univer-cursor-pointer univer-items-center univer-justify-center univer-border-none hover:univer-opacity-70"'
);
});
it('should normalize whitespace for compiled className properties', () => {
const sourceCode = `
jsx("button", {
className: "\\n univer-flex univer-cursor-pointer univer-items-center univer-justify-center\\n univer-border-none\\n hover:univer-opacity-70\\n ",
type: "button",
});
`;
expect(cleanupClassNameTemplateWhitespace(sourceCode, '/tmp/example.js')).toContain(
'className: "univer-flex univer-cursor-pointer univer-items-center univer-justify-center univer-border-none hover:univer-opacity-70"'
);
});
it('should normalize whitespace inside cva definitions', () => {
const sourceCode = `
const buttonVariants = cva(
\`
univer-box-border univer-inline-flex univer-cursor-pointer
disabled:univer-pointer-events-none
\`,
{
variants: {
variant: {
primary: \`
univer-border-primary-600 univer-bg-primary-600 univer-text-gray-0
hover:univer-bg-primary-500
\`,
},
},
compoundVariants: [{
className: \`
univer-gap-1 univer-rounded
dark:!univer-bg-gray-700
\`,
}],
}
);
`;
const cleanedCode = cleanupClassNameTemplateWhitespace(sourceCode, '/tmp/example.tsx');
expect(cleanedCode).toContain(
'cva(\n "univer-box-border univer-inline-flex univer-cursor-pointer disabled:univer-pointer-events-none",'
);
expect(cleanedCode).toContain(
'primary: "univer-border-primary-600 univer-bg-primary-600 univer-text-gray-0 hover:univer-bg-primary-500"'
);
expect(cleanedCode).toContain(
'className: "univer-gap-1 univer-rounded dark:!univer-bg-gray-700"'
);
});
it('should not touch unrelated template literals', () => {
const sourceCode = 'const message = ` hello\\n world `;';
expect(cleanupClassNameTemplateWhitespace(sourceCode, '/tmp/example.ts')).toBe(sourceCode);
});
});

View File

@@ -0,0 +1,175 @@
import path from 'node:path';
import * as ts from 'typescript';
interface ITextEdit {
end: number;
start: number;
text: string;
}
const SUPPORTED_SCRIPT_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx']);
function stripQueryAndHash(filePath: string) {
return filePath.replace(/[?#].*$/, '');
}
function shouldProcessFile(filePath: string) {
const normalizedPath = stripQueryAndHash(filePath);
if (normalizedPath.includes('/node_modules/')) {
return false;
}
return SUPPORTED_SCRIPT_EXTENSIONS.has(path.extname(normalizedPath));
}
function getScriptKind(filePath: string) {
switch (path.extname(stripQueryAndHash(filePath))) {
case '.jsx':
return ts.ScriptKind.JSX;
case '.tsx':
return ts.ScriptKind.TSX;
case '.js':
return ts.ScriptKind.JS;
default:
return ts.ScriptKind.TS;
}
}
function normalizeClassNameWhitespace(value: string) {
return value.replace(/\s+/g, ' ').trim();
}
function mayContainCleanupTarget(sourceCode: string) {
return /(?:className|clsx\s*\(|cva\s*\()/.test(sourceCode)
&& /(?:\s{2,}|\\[nr])/.test(sourceCode);
}
function isClsxIdentifier(node: ts.Expression) {
return ts.isIdentifier(node) && node.text === 'clsx';
}
function isCvaIdentifier(node: ts.Expression) {
return ts.isIdentifier(node) && node.text === 'cva';
}
function isStaticStringLiteral(node: ts.Node): node is ts.NoSubstitutionTemplateLiteral | ts.StringLiteral {
return ts.isNoSubstitutionTemplateLiteral(node) || ts.isStringLiteral(node);
}
function isClassNameIdentifier(name: ts.PropertyName | ts.JsxAttributeName) {
return ts.isIdentifier(name) && name.text === 'className';
}
function isClsxArgument(node: ts.Expression) {
let current: ts.Node = node;
while (current.parent) {
if (ts.isCallExpression(current.parent)) {
return current.parent.arguments.includes(current as ts.Expression)
&& isClsxIdentifier(current.parent.expression);
}
current = current.parent;
}
return false;
}
function isClassNameJsxAttributeValue(node: ts.Expression) {
if (ts.isJsxAttribute(node.parent)) {
return node.parent.initializer === node && isClassNameIdentifier(node.parent.name);
}
return ts.isJsxExpression(node.parent)
&& node.parent.expression === node
&& ts.isJsxAttribute(node.parent.parent)
&& isClassNameIdentifier(node.parent.parent.name);
}
function isClassNamePropertyValue(node: ts.Expression) {
return ts.isPropertyAssignment(node.parent)
&& node.parent.initializer === node
&& (
isClassNameIdentifier(node.parent.name)
|| (ts.isStringLiteral(node.parent.name) && node.parent.name.text === 'className')
);
}
function isInsideCvaCall(node: ts.Node) {
let current: ts.Node | undefined = node.parent;
while (current) {
if (ts.isCallExpression(current) && isCvaIdentifier(current.expression)) {
return true;
}
current = current.parent;
}
return false;
}
function isClassNameWhitespaceTarget(node: ts.NoSubstitutionTemplateLiteral | ts.StringLiteral) {
return isClsxArgument(node)
|| isClassNameJsxAttributeValue(node)
|| isClassNamePropertyValue(node)
|| isInsideCvaCall(node);
}
function applyTextEdits(sourceCode: string, edits: ITextEdit[]) {
return edits
.sort((left, right) => right.start - left.start)
.reduce((code, edit) => `${code.slice(0, edit.start)}${edit.text}${code.slice(edit.end)}`, sourceCode);
}
export function cleanupClassNameTemplateWhitespace(sourceCode: string, filePath: string) {
if (!shouldProcessFile(filePath) || !mayContainCleanupTarget(sourceCode)) {
return sourceCode;
}
const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true, getScriptKind(filePath));
const edits: ITextEdit[] = [];
function visit(node: ts.Node) {
if (isStaticStringLiteral(node) && isClassNameWhitespaceTarget(node)) {
const normalized = normalizeClassNameWhitespace(node.text);
if (normalized !== node.text) {
edits.push({
end: node.end,
start: node.getStart(sourceFile),
text: JSON.stringify(normalized),
});
}
}
ts.forEachChild(node, visit);
}
visit(sourceFile);
if (edits.length === 0) {
return sourceCode;
}
return applyTextEdits(sourceCode, edits);
}
export function createClassNameWhitespaceCleanupPlugin() {
return {
name: 'class-name-whitespace-cleanup',
transform(sourceCode: string, id: string) {
const cleanedCode = cleanupClassNameTemplateWhitespace(sourceCode, id);
if (cleanedCode === sourceCode) {
return null;
}
return {
code: cleanedCode,
map: null,
};
},
};
}

View File

@@ -0,0 +1,95 @@
import type { UserConfig } from 'tsdown';
const CSS_NOOP_PLUGIN_NAME = 'univer-css-noop';
const CSS_IMPORT_RE = /\.(?:css|less|sass|scss|styl|stylus)(?:$|\?)/;
const CSS_NOOP_ID_PREFIX = '\0univer-css-noop:';
const CSS_NOOP_ID_RE = /^\0univer-css-noop:/;
type TInputOptions = NonNullable<UserConfig['inputOptions']>;
type TInputOptionsFunction = Extract<TInputOptions, (...args: any[]) => any>;
type TInputOptionsObject = Exclude<TInputOptions, (...args: any[]) => any>;
function toPluginArray(plugins: unknown): any[] {
return Array.isArray(plugins)
? plugins
: plugins
? [plugins]
: [];
}
function withoutCssNoopPlugin(plugins: any[]) {
return plugins.filter((plugin) => !plugin || plugin.name !== CSS_NOOP_PLUGIN_NAME);
}
export function createCssNoopPlugin() {
let nextId = 0;
return {
name: CSS_NOOP_PLUGIN_NAME,
resolveId: {
filter: { id: CSS_IMPORT_RE },
handler() {
return `${CSS_NOOP_ID_PREFIX}${nextId++}`;
},
},
load: {
filter: { id: CSS_NOOP_ID_RE },
handler() {
return {
code: 'export default {};',
moduleSideEffects: false,
moduleType: 'js',
};
},
},
};
}
function prependCssNoopPlugin(plugins: any[]) {
return [
createCssNoopPlugin(),
...withoutCssNoopPlugin(plugins),
];
}
export function createCssNoopInputOptions(inputOptions?: TInputOptions): TInputOptionsFunction {
return (async (defaultOptions: { plugins?: unknown }, ...args: any[]) => {
const defaultPlugins = toPluginArray(defaultOptions.plugins);
if (!inputOptions) {
return {
...defaultOptions,
plugins: prependCssNoopPlugin(defaultPlugins),
};
}
if (typeof inputOptions === 'function') {
const defaultOptionsWithCssNoop = {
...defaultOptions,
plugins: prependCssNoopPlugin(defaultPlugins),
};
const resolvedOptions = await (inputOptions as any)(defaultOptionsWithCssNoop, ...args);
if (!resolvedOptions) {
return defaultOptionsWithCssNoop;
}
return {
...defaultOptionsWithCssNoop,
...resolvedOptions,
plugins: prependCssNoopPlugin(toPluginArray(resolvedOptions.plugins ?? defaultOptionsWithCssNoop.plugins)),
};
}
const inputOptionsObject = inputOptions as TInputOptionsObject & { plugins?: unknown };
return {
...defaultOptions,
...inputOptionsObject,
plugins: prependCssNoopPlugin([
...toPluginArray(inputOptionsObject.plugins),
...defaultPlugins,
]),
};
}) as TInputOptionsFunction;
}

View File

@@ -0,0 +1,58 @@
import { cpSync, existsSync, mkdirSync, unlinkSync } from 'node:fs';
import path from 'node:path';
interface IBundleOutput {
fileName?: string;
}
export interface ICreateOutputAliasPluginOptions {
copyToRoot: boolean;
keepRootIndexCss: boolean;
packageDir: string;
}
export function createOutputAliasPlugin(options: ICreateOutputAliasPluginOptions) {
const { copyToRoot, keepRootIndexCss, packageDir } = options;
return {
name: 'create-output-alias',
writeBundle(outputOptions: { dir?: string }, bundle: Record<string, IBundleOutput>) {
if (!outputOptions.dir) {
return;
}
const outDir = path.resolve(packageDir, outputOptions.dir);
const rootDir = path.resolve(outDir, '..');
for (const output of Object.values(bundle)) {
if (!output.fileName || output.fileName.endsWith('.map')) {
continue;
}
const sourcePath = path.join(outDir, output.fileName);
if (output.fileName.endsWith('.css')) {
if (keepRootIndexCss) {
const rootCssPath = path.join(rootDir, 'index.css');
mkdirSync(path.dirname(rootCssPath), { recursive: true });
cpSync(sourcePath, rootCssPath);
}
if (existsSync(sourcePath)) {
unlinkSync(sourcePath);
}
continue;
}
if (!copyToRoot) {
continue;
}
const targetPath = path.join(rootDir, output.fileName);
mkdirSync(path.dirname(targetPath), { recursive: true });
cpSync(sourcePath, targetPath);
}
},
};
}

View File

@@ -0,0 +1,42 @@
import type { ObfuscatorOptions } from 'javascript-obfuscator';
import { readFileSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import JavaScriptObfuscator from 'javascript-obfuscator';
interface IBundleOutput {
fileName?: string;
}
export function createOutputObfuscatorPlugin(ignorePatterns?: RegExp[]) {
const obfuscationOptions: ObfuscatorOptions = {
ignoreImports: true,
};
return {
name: 'create-output-obfuscator',
writeBundle(outputOptions: { dir?: string }, bundle: Record<string, IBundleOutput>) {
if (!outputOptions.dir) {
return;
}
const outDir = outputOptions.dir;
for (const output of Object.values(bundle)) {
const fileName = output.fileName;
if (!fileName || !fileName.endsWith('.js') || fileName.endsWith('.map')) {
continue;
}
if (ignorePatterns?.some((pattern) => pattern.test(fileName))) {
continue;
}
const targetPath = path.resolve(outDir, fileName);
const sourceCode = readFileSync(targetPath, 'utf8');
const obfuscatedCode = JavaScriptObfuscator.obfuscate(sourceCode, obfuscationOptions).getObfuscatedCode();
writeFileSync(targetPath, obfuscatedCode, 'utf8');
}
},
};
}