Skip to content

Commit

Permalink
Merge branch 'umijs:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
gwuhaolin authored Oct 19, 2023
2 parents 7530f81 + a9dd52a commit 7ff8d65
Show file tree
Hide file tree
Showing 19 changed files with 282 additions and 47 deletions.
2 changes: 1 addition & 1 deletion docs/docs/docs/api/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -1384,7 +1384,7 @@ styles: [`body { color: red; }`, `https://a.com/b.css`],

## srcTranspiler

- 类型:`string` 可选的值:`babel`, `swc`, `esbuild`, `none`
- 类型:`string` 可选的值:`babel`, `swc`, `esbuild`
- 默认值:`babel`

配置构建时转译 js/ts 的工具。
Expand Down
1 change: 0 additions & 1 deletion packages/bundler-webpack/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ export function getSchemas(): Record<string, (arg: { zod: typeof z }) => any> {
Transpiler.babel,
Transpiler.esbuild,
Transpiler.swc,
Transpiler.none,
]),
srcTranspilerOptions: ({ zod }) =>
zod
Expand Down
7 changes: 7 additions & 0 deletions packages/preset-umi/fixtures/overrides/less/index.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@import "foo/foo.less";

@red: green;
.a {
aaa: @red;
bbb: @primary-color;
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.
Empty file.
Empty file.
3 changes: 3 additions & 0 deletions packages/preset-umi/fixtures/overrides/normal/foo/bar/hoo.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.hoo {
color: red;
}
2 changes: 2 additions & 0 deletions packages/preset-umi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@
"enhanced-resolve": "5.9.3",
"fast-glob": "3.2.12",
"html-webpack-plugin": "5.5.0",
"less-plugin-resolve": "1.0.0",
"path-to-regexp": "1.7.0",
"postcss": "^8.4.21",
"postcss-prefix-selector": "1.16.0",
"react": "18.1.0",
"react-dom": "18.1.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import assert from 'assert';
import fs from 'fs';
import { join } from 'path';
import { compileLess } from './compileLess';

const fixturesDir = join(__dirname, '../../../fixtures');

// 在 jest 下跑会出错,所以只能手动跑来验证了
// test('normal', async () => {
(async () => {
const filePath = join(fixturesDir, 'overrides/less/index.less');
const modifyVars = {
'primary-color': '#1DA57A',
};
const alias = {
barbar: join(filePath, '../node_modules/bar'),
};
const result = await compileLess(
fs.readFileSync(filePath, 'utf-8'),
filePath,
modifyVars,
alias,
);
assert(
result.includes(
`
.bar {
color: red;
}
.foo {
color: red;
}
.a {
aaa: green;
bbb: #1DA57A;
}
`.trim(),
),
);
})().catch((e) => {
console.error(e);
});
// });
20 changes: 20 additions & 0 deletions packages/preset-umi/src/features/overrides/compileLess.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import less from '@umijs/bundler-utils/compiled/less';

export async function compileLess(
lessContent: string,
filePath: string,
modifyVars: Record<string, string> = {},
alias: Record<string, string> = {},
) {
const result = await less.render(lessContent, {
filename: filePath,
plugins: [
new (require('less-plugin-resolve') as any)({
aliases: alias,
}),
],
javascriptEnabled: true,
modifyVars,
});
return result.css;
}
70 changes: 34 additions & 36 deletions packages/preset-umi/src/features/overrides/overrides.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,48 @@
import { winPath } from '@umijs/utils';
import { existsSync } from 'fs';
import { existsSync, readFileSync } from 'fs';
import { join } from 'path';
import { expandCSSPaths } from '../../commands/dev/watch';
import type { IApi } from '../../types';
import { compileLess } from './compileLess';
import { transform } from './transform';

export function getOverridesCSS(absSrcPath: string) {
return expandCSSPaths(join(absSrcPath, 'overrides')).find(existsSync);
}

export default (api: IApi) => {
api.modifyConfig((memo) => {
if (getOverridesCSS(api.paths.absSrcPath)) {
memo.extraPostCSSPlugins ??= [];
memo.extraPostCSSPlugins.push(
// prefix #root for overrides.{ext} style file, to make sure selector priority is higher than async chunk style
require('postcss-prefix-selector')({
// why not #root?
// antd will insert dom into body, prefix #root will not works for that
prefix: 'body',
transform(
_p: string,
selector: string,
prefixedSelector: string,
filePath: string,
) {
const isOverridesFile =
winPath(api.appData.overridesCSS[0]) === winPath(filePath);

if (isOverridesFile) {
if (selector === 'html') {
// special :first-child to promote html selector priority
return `html:first-child`;
} else if (/^body([\s+~>[:]|$)/.test(selector)) {
// use html parent to promote body selector priority
return `html ${selector}`;
}

return prefixedSelector;
}

return selector;
let cachedContent: string | null = null;
api.onGenerateFiles(async () => {
if (api.appData.overridesCSS.length) {
const filePath = api.appData.overridesCSS[0];
let content = readFileSync(filePath, 'utf-8');
if (content === cachedContent) return;
const isLess = filePath.endsWith('.less');
if (isLess) {
content = await compileLess(
content,
filePath,
{
...api.config.theme,
...api.config.lessLoader?.modifyVars,
},
}),
);
api.config.alias,
);
}
content = await transform(content, filePath);
api.writeTmpFile({
path: 'core/overrides.css',
content,
noPluginDir: true,
});
cachedContent = content;
}
});

return memo;
api.addEntryImports(() => {
return [
api.appData.overridesCSS.length && {
source: '@@/core/overrides.css',
},
].filter(Boolean);
});
};
79 changes: 79 additions & 0 deletions packages/preset-umi/src/features/overrides/transform.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { winPath } from '@umijs/utils';
import { join } from 'path';
import { transform } from './transform';

const fixturesDir = join(__dirname, '../../../fixtures');

test('transform selector', async () => {
const result = await transform(
`
html {}
.a {}
#b {}
div {}
@media (max-width: 100px) {
.b {}
}
`,
'/foo/bar/hoo.css',
);
expect(result).toEqual(`
html:first-child {}
body .a {}
body #b {}
body div {}
@media (max-width: 100px) {
body .b {}
}
`);
});

test('transform import', async () => {
const filePath = join(fixturesDir, 'overrides/normal/foo/bar/hoo.css');
const result = await transform(
`
@import "a";
@import "~b";
@import "./a.css";
@import './a.css';
@import "../a.css";
@import "../not-exists.css";
@import "a.css";
@import "child/a.css";
@import "child/not-exists.css";
`,
filePath,
);
expect(result.replace(new RegExp(`${winPath(fixturesDir)}`, 'g'), ''))
.toEqual(`
@import "a";
@import "~b";
@import "/overrides/normal/foo/bar/a.css";
@import "/overrides/normal/foo/bar/a.css";
@import "/overrides/normal/foo/a.css";
@import "../not-exists.css";
@import "/overrides/normal/foo/bar/a.css";
@import "/overrides/normal/foo/bar/child/a.css";
@import "child/not-exists.css";
`);
});

test('transform import with url', async () => {
const filePath = join(fixturesDir, 'overrides/normal/foo/bar/hoo.css');
const result = await transform(
`
@import url("a.css");
@import url('a.css');
@import url(a.css);
@import url(not-exists.css);
`,
filePath,
);
expect(result.replace(new RegExp(`${winPath(fixturesDir)}`, 'g'), ''))
.toEqual(`
@import "/overrides/normal/foo/bar/a.css";
@import "/overrides/normal/foo/bar/a.css";
@import "/overrides/normal/foo/bar/a.css";
@import url(not-exists.css);
`);
});
59 changes: 59 additions & 0 deletions packages/preset-umi/src/features/overrides/transform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { winPath } from '@umijs/utils';
import fs from 'fs';
import path from 'path';
import type { AtRule } from 'postcss';

export async function transform(cssContent: string, filePath: string) {
const importPlugin = {
postcssPlugin: 'importPlugin',
AtRule: {
import(atRule: AtRule) {
let origin = atRule.params;
// remove url() wrapper
origin = origin.replace(/^url\((.+)\)$/g, '$1');
// remove start ' or " and end ' or "
origin = origin.replace(/^'(.+)'$/g, '$1').replace(/^"(.+)"$/g, '$1');

// ~ 开头的肯定是从 node_modules 下查找
if (origin.startsWith('~')) return;
if (origin.startsWith('/')) return;

function getResolvedPath(origin: string) {
return winPath(path.resolve(path.dirname(filePath), origin));
}

// 已经包含 ./ 和 ../ 的场景,不需要额外处理
// CSS 会优先找相对路径,如果找不到,会再找 node_modules 下的
const resolvedPath = getResolvedPath(origin);
if (fs.existsSync(resolvedPath)) {
atRule.params = `"${resolvedPath}"`;
} else {
// Warn user if file not exists, but it should be existed
if (origin.startsWith('./') || origin.startsWith('../')) {
console.warn(`File does not exist: ${resolvedPath}`);
}
}
},
},
};
const selectorPlugin = require('postcss-prefix-selector')({
// why not #root?
// antd will insert dom into body, prefix #root will not work for that
prefix: 'body',
transform(_p: string, selector: string, prefixedSelector: string) {
if (selector === 'html') {
// special :first-child to promote html selector priority
return `html:first-child`;
} else if (/^body([\s+~>[:]|$)/.test(selector)) {
// use html parent to promote body selector priority
return `html ${selector}`;
}
return prefixedSelector;
},
});
const result = await require('postcss')([
selectorPlugin,
importPlugin,
]).process(cssContent, {});
return result.css;
}
7 changes: 1 addition & 6 deletions packages/preset-umi/src/features/tmpFiles/tmpFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,12 +305,7 @@ declare module '*.txt' {
imports: importsToStr(
await api.applyPlugins({
key: 'addEntryImports',
initialValue: [
// append overrides.{ext} style file
api.appData.overridesCSS.length && {
source: api.appData.overridesCSS[0],
},
].filter(Boolean),
initialValue: [],
}),
).join('\n'),
basename: api.config.base,
Expand Down
Loading

0 comments on commit 7ff8d65

Please sign in to comment.