-
Notifications
You must be signed in to change notification settings - Fork 4
/
rollup-plugin-import-styles.ts
167 lines (148 loc) · 4.46 KB
/
rollup-plugin-import-styles.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import MagicString from "magic-string";
import postcss from "postcss";
import Processor from "postcss/lib/processor";
import { ResolveIdResult, SourceDescription } from "rollup";
import { emptySourcemap } from "../util";
import sass from "node-sass";
import { resolve, dirname } from "path";
export type Transformer = (css: string) => string;
export type GetTransformer = (id: string, isGlobal: boolean) => Transformer;
export interface IRollupPluginImportStylesConfig {
// Postcss plugins.
plugins: any[];
// File types handled by this plugin.
extensions: string[];
// Global files that are injected into the DOM.
globals: string[];
// Configuration objects for sass and postcss
postcssConfig: any;
sassConfig: any;
// Transform function for the styles
transform: GetTransformer;
}
/**
* Default configuration for the import SCSS plugin.
* @type {{plugins: Array, extensions: string[], globals: Array}}
*/
const defaultConfig: IRollupPluginImportStylesConfig = {
plugins: [],
extensions: [".css", ".scss"],
globals: [],
postcssConfig: {},
sassConfig: {},
transform: transformImport
};
/**
* Default transform.
* @param id
* @param isGlobal
*/
function transformImport(id: string, isGlobal: boolean) {
return isGlobal ? transformGlobal : transformDefault;
}
/**
* Overwrites the css file with "export default".
* @param css
* @returns {string}
*/
function transformDefault(css: string): string {
return `export default \`${css}\``;
}
/**
* Overwrites the css file with a global inject into the head.
* @param css
* @returns {string}
*/
function transformGlobal(css: string): string {
return `
const css = \`${css}\`;
const $styles = document.createElement("style");
$styles.innerText = css;
document.head.appendChild($styles);
export default css;
`;
}
/**
* Processes a SCSS file by running it through a processor and generating the code and its corresponding sourcemap.
* @param data
* @param id
* @param processor
* @param overwrite
* @param postcssConfig
* @param sassConfig
*/
async function processFile({
data,
id,
processor,
overwrite,
postcssConfig,
sassConfig
}: IRollupPluginImportStylesConfig & { overwrite: Transformer; data: string; id: string; processor: Processor }) {
return new Promise(res => {
// Compile the data using the sass compiler
const css = sass
.renderSync({
file: resolve(id),
sourceMap: false /* We generate sourcemaps later */,
...(sassConfig || {})
})
.css.toString();
// The magic strings cannot handle empty strings, therefore we test whether we should already abort now.
if (css.trim() === "") {
return res({
code: overwrite(""),
map: emptySourcemap
});
}
// Create a magic string container to generate source map
const stringContainer = new MagicString(css);
// Construct the options
const processOptions = {
from: id,
to: id,
map: {
inline: false,
annotation: false
},
...(postcssConfig || {})
};
// Process the file content
processor.process(stringContainer.toString(), processOptions).then(result => {
const css = result.css;
stringContainer.overwrite(0, stringContainer.length(), overwrite(css));
res({
code: stringContainer.toString(),
map: stringContainer.generateMap()
});
});
});
}
/**
* A Rollup plugin that makes it possible to import style files using postcss.
* Looks for the "import css from 'styles.scss'" and "import 'styles.scss'" syntax as default.
* @param config
*/
export function importStyles(config: Partial<IRollupPluginImportStylesConfig> = {}) {
config = { ...defaultConfig, ...config };
const { plugins, extensions, globals, transform } = config as IRollupPluginImportStylesConfig;
// Determines whether the file should be handled by the plugin or not.
const filter = (id: string) => extensions.find(ext => id.endsWith(ext)) != null;
// Determines whether the file is global or not
const isGlobal = (id: string) => globals.find(name => id.endsWith(name)) != null;
// Create the postcss processor based on the plugins.
const processor = postcss(plugins);
return {
name: "importStyles",
resolveId: (id: string, importer: string): ResolveIdResult => {
if (!importer || !filter(id)) return;
return resolve(dirname(importer), id);
},
transform: async (data: string, id: string): Promise<SourceDescription | string | void> => {
if (!filter(id)) return;
const overwrite = transform(id, isGlobal(id));
// @ts-ignore
return processFile({ ...config, processor, overwrite, id, data });
}
};
}