Skip to content

Commit 3e0393c

Browse files
authored
Support css imports in CRA (codesandbox#3837)
* Support css imports in CRA * Fix absolute paths * Make PostCSS its own processor
1 parent 5cbe097 commit 3e0393c

File tree

5 files changed

+161
-3
lines changed

5 files changed

+161
-3
lines changed

packages/app/src/sandbox/eval/presets/create-react-app/v3.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import rawTranspiler from '../../transpilers/raw';
1111
import svgrTranspiler from '../../transpilers/svgr';
1212
import sassTranspiler from '../../transpilers/sass';
1313
import refreshTranspiler from '../../transpilers/react/refresh-transpiler';
14+
import styleProcessor from '../../transpilers/postcss';
1415
import {
1516
hasRefresh,
1617
aliases,
@@ -190,6 +191,7 @@ export default function initialize() {
190191
module => /\.module\.s[c|a]ss$/.test(module.path),
191192
[
192193
{ transpiler: sassTranspiler },
194+
{ transpiler: styleProcessor },
193195
{
194196
transpiler: stylesTranspiler,
195197
options: { module: true, hmrEnabled: isRefresh },
@@ -199,6 +201,7 @@ export default function initialize() {
199201
preset.registerTranspiler(
200202
module => /\.module\.css$/.test(module.path),
201203
[
204+
{ transpiler: styleProcessor },
202205
{
203206
transpiler: stylesTranspiler,
204207
options: { module: true, hmrEnabled: isRefresh },
@@ -207,13 +210,15 @@ export default function initialize() {
207210
);
208211

209212
preset.registerTranspiler(module => /\.css$/.test(module.path), [
213+
{ transpiler: styleProcessor },
210214
{
211215
transpiler: stylesTranspiler,
212216
options: { hmrEnabled: isRefresh },
213217
},
214218
]);
215219
preset.registerTranspiler(module => /\.s[c|a]ss$/.test(module.path), [
216220
{ transpiler: sassTranspiler },
221+
{ transpiler: styleProcessor },
217222
{
218223
transpiler: stylesTranspiler,
219224
options: { hmrEnabled: isRefresh },
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import Transpiler from '..';
2+
import { LoaderContext } from '../../transpiled-module';
3+
4+
/**
5+
* Mainly responsible for inlining css imports
6+
*/
7+
class PostCSSCompiler extends Transpiler {
8+
constructor() {
9+
super('postcss-compiler');
10+
}
11+
12+
doTranspilation(code: string, loaderContext: LoaderContext) {
13+
return import(
14+
/* webpackChunkName: 'postcss-compiler' */ './loader'
15+
).then(loader => loader.default(code, loaderContext));
16+
}
17+
}
18+
19+
const transpiler = new PostCSSCompiler();
20+
21+
export { PostCSSCompiler };
22+
23+
export default transpiler;
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import postcss, { ProcessOptions } from 'postcss';
2+
import postcssImportPlugin from 'postcss-import';
3+
import { join } from 'path';
4+
import { isDependencyPath } from 'sandbox/eval/utils/is-dependency-path';
5+
6+
import TranspiledModule, { LoaderContext } from '../../transpiled-module';
7+
8+
async function resolveCSSFile(
9+
loaderContext: LoaderContext,
10+
path: string,
11+
basePath: string
12+
): Promise<TranspiledModule> {
13+
const isDependency = isDependencyPath(path);
14+
15+
if (isDependency) {
16+
// First try to resolve the package.json, in case it has a style field
17+
try {
18+
const pkgJson = await loaderContext.resolveTranspiledModuleAsync(
19+
join(path, 'package.json')
20+
);
21+
const parsedPkg = JSON.parse(pkgJson.module.code);
22+
23+
if (parsedPkg.style) {
24+
const fullPath = join(path, parsedPkg.style);
25+
26+
return loaderContext.resolveTranspiledModuleAsync(fullPath);
27+
}
28+
} catch (e) {
29+
/* Move to step 2 */
30+
}
31+
32+
return loaderContext.resolveTranspiledModuleAsync(path);
33+
}
34+
35+
const fullPath = path.charAt(0) === '/' ? path : join(basePath, path);
36+
return loaderContext.resolveTranspiledModuleAsync(fullPath);
37+
}
38+
39+
export default function(
40+
code: string,
41+
loaderContext: LoaderContext
42+
): Promise<{ transpiledCode: string; sourceMap: any }> {
43+
return new Promise((resolve, reject) => {
44+
const plugins = [
45+
postcssImportPlugin({
46+
resolve: async (id: string, root: string) => {
47+
try {
48+
const result = await resolveCSSFile(loaderContext, id, root);
49+
50+
return result.module.path;
51+
} catch (e) {
52+
return null;
53+
}
54+
},
55+
load: async (filename: string) => {
56+
const tModule = await loaderContext.resolveTranspiledModuleAsync(
57+
filename
58+
);
59+
60+
return tModule.module.code;
61+
},
62+
}),
63+
];
64+
65+
const options: ProcessOptions = {
66+
to: loaderContext.path,
67+
from: loaderContext.path,
68+
map: {
69+
inline: true,
70+
annotation: true,
71+
},
72+
};
73+
74+
return (
75+
postcss(plugins)
76+
// Explcitly give undefined if code is null, otherwise postcss crashses
77+
.process(code === null ? undefined : code, options)
78+
.then(result => {
79+
if (result.messages) {
80+
const messages = result.messages as any[];
81+
messages.forEach(m => {
82+
if (m.type === 'dependency') {
83+
loaderContext.addDependency(m.file);
84+
}
85+
});
86+
}
87+
88+
const map = result.map && result.map.toJSON();
89+
resolve({ transpiledCode: result.css, sourceMap: map });
90+
91+
return null; // silence bluebird warning
92+
})
93+
.catch(err => reject(err))
94+
);
95+
});
96+
}

packages/app/src/sandbox/eval/transpilers/vue/style-compiler/loader.ts

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,44 @@
11
import postcss, { ProcessOptions } from 'postcss';
22
import postcssImportPlugin from 'postcss-import';
3+
import { join } from 'path';
4+
import { isDependencyPath } from 'sandbox/eval/utils/is-dependency-path';
35

4-
import { LoaderContext } from '../../../transpiled-module';
6+
import TranspiledModule, { LoaderContext } from '../../../transpiled-module';
57

68
import trim from './plugins/trim';
79
import scopeId from './plugins/scope-id';
810

11+
async function resolveCSSFile(
12+
loaderContext: LoaderContext,
13+
path: string,
14+
basePath: string
15+
): Promise<TranspiledModule> {
16+
const isDependency = isDependencyPath(path);
17+
18+
if (isDependency) {
19+
// First try to resolve the package.json, in case it has a style field
20+
try {
21+
const pkgJson = await loaderContext.resolveTranspiledModuleAsync(
22+
join(path, 'package.json')
23+
);
24+
const parsedPkg = JSON.parse(pkgJson.module.code);
25+
26+
if (parsedPkg.style) {
27+
const fullPath = join(path, parsedPkg.style);
28+
29+
return loaderContext.resolveTranspiledModuleAsync(fullPath);
30+
}
31+
} catch (e) {
32+
/* Move to step 2 */
33+
}
34+
35+
return loaderContext.resolveTranspiledModuleAsync(path);
36+
}
37+
38+
const fullPath = path.charAt(0) === '/' ? path : join(basePath, path);
39+
return loaderContext.resolveTranspiledModuleAsync(fullPath);
40+
}
41+
942
export default function(
1043
code: string,
1144
loaderContext: LoaderContext
@@ -24,9 +57,9 @@ export default function(
2457
// TODO autoprefixer
2558
const plugins = [
2659
postcssImportPlugin({
27-
resolve: async (id: string) => {
60+
resolve: async (id: string, root: string) => {
2861
try {
29-
const result = await loaderContext.resolveTranspiledModuleAsync(id);
62+
const result = await resolveCSSFile(loaderContext, id, root);
3063

3164
return result.module.path;
3265
} catch (e) {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const isDependencyPath = (p: string) => /^(\w|@\w)/.test(p);

0 commit comments

Comments
 (0)