@mlut/plugins 1.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/README.md +109 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +135 -0
- package/dist/transformCss.d.ts +6 -0
- package/dist/transformCss.js +64 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# mlut plugins #
|
|
2
|
+
|
|
3
|
+
<img alt="Logo" src="https://github.com/mr150/mlut/raw/master/docs/img/logo-full.png" width="350"/>
|
|
4
|
+
|
|
5
|
+
The [mlut](https://github.com/mr150/mlut) plugins for Rollup, Vite and Webpack. Based on [unplugin](https://unplugin.unjs.io/).
|
|
6
|
+
|
|
7
|
+
## Installation ##
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm i -D @mlut/plugins sass-embedded
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
When using this package, you will need to install Sass separately. We recommend [sass-embedded](https://www.npmjs.com/package/sass-embedded), but regular [sass](https://www.npmjs.com/package/sass) is also suitable.
|
|
14
|
+
|
|
15
|
+
This allows you to control the versions of all your dependencies, and to choose which Sass implementation to use.
|
|
16
|
+
|
|
17
|
+
## Usage ##
|
|
18
|
+
|
|
19
|
+
Import the plugin for the appropriate bundler from the `@mlut/plugins` package as in one of the examples below. You can find more detailed examples using dev-server and livereload in the plugin tests [directory](https://github.com/mr150/mlut/tree/master/test/plugins)
|
|
20
|
+
|
|
21
|
+
### Rollup ###
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
// rollup.config.js
|
|
25
|
+
import { rollup } from '@mlut/plugins';
|
|
26
|
+
|
|
27
|
+
const mlut = rollup({
|
|
28
|
+
output: 'dist/style.css',
|
|
29
|
+
// other options...
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export default {
|
|
33
|
+
// rollup config...
|
|
34
|
+
plugins: [mlut],
|
|
35
|
+
};
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Vite ###
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
// vite.config.js
|
|
42
|
+
import { vite } from '@mlut/plugins';
|
|
43
|
+
|
|
44
|
+
const mlut = vite({
|
|
45
|
+
output: 'dist/assets/style.css',
|
|
46
|
+
// other options...
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export default defineConfig(() => {
|
|
50
|
+
return {
|
|
51
|
+
// vite config...
|
|
52
|
+
plugins: [mlut],
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Webpack ###
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
// webpack.config.js
|
|
61
|
+
import { webpack } from '@mlut/plugins';
|
|
62
|
+
|
|
63
|
+
const mlut = webpack({
|
|
64
|
+
output: 'dist/style.css',
|
|
65
|
+
// other options...
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export default {
|
|
69
|
+
// webpack config...
|
|
70
|
+
plugins: [mlut],
|
|
71
|
+
};
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Options ##
|
|
75
|
+
|
|
76
|
+
The plugin options are similar to the mlut CLI options
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
interface Options {
|
|
80
|
+
output: string,
|
|
81
|
+
input?: string,
|
|
82
|
+
minify?: boolean,
|
|
83
|
+
autoprefixer?: boolean,
|
|
84
|
+
noMergeMq?: boolean,
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- `output` - output CSS file
|
|
89
|
+
- `input` - input Sass file when you import mlut, configure it and write other CSS
|
|
90
|
+
- `minify` - generate minified CSS. For this option to work, you need 1 of the following minifiers: [csso](https://github.com/css/csso), [lightningcss](https://github.com/parcel-bundler/lightningcss), [clean-css](https://github.com/clean-css/clean-css), [cssnano](https://github.com/cssnano/cssnano) or [esbuild](https://github.com/evanw/esbuild). You may already have it installed
|
|
91
|
+
- `autoprefixer` - whether to add vendor prefixes to CSS properties. You need the [autoprefixer](https://github.com/postcss/autoprefixer) package or lightningcss for this option to work
|
|
92
|
+
- `noMergeMq` - prevent merging of CSS media queries during minification. Relevant only when using the csso minifier
|
|
93
|
+
|
|
94
|
+
You can add the options in your input Sass file too. Options must be a **valid JSON**, but single quotes is allowed. Paths will be resolved relative to the JIT engine working directory
|
|
95
|
+
```scss
|
|
96
|
+
@use '@mlut/core' with (
|
|
97
|
+
$jit: (
|
|
98
|
+
'output': 'dist/assets/style.css',
|
|
99
|
+
'minify': true,
|
|
100
|
+
'autoprefixer': true
|
|
101
|
+
),
|
|
102
|
+
);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Documentation ##
|
|
106
|
+
Full documentation available [here](https://mr150.github.io/mlut/section-start.html#kssref-start-integrations)
|
|
107
|
+
|
|
108
|
+
## License ##
|
|
109
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TransformCssOptions } from './transformCss.js';
|
|
2
|
+
export interface Options extends TransformCssOptions {
|
|
3
|
+
readonly input?: string;
|
|
4
|
+
readonly output: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const unplugin: import("unplugin").UnpluginInstance<Options, boolean>;
|
|
7
|
+
export declare const rollup: (options: Options) => import("rollup").Plugin<any> | import("rollup").Plugin<any>[];
|
|
8
|
+
export declare const vite: (options: Options) => import("vite").Plugin<any> | import("vite").Plugin<any>[];
|
|
9
|
+
export declare const webpack: (options: Options) => import("webpack").WebpackPluginInstance;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { jitEngine, logger } from '@mlut/core';
|
|
2
|
+
import { createUnplugin } from 'unplugin';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import { transformCss } from './transformCss.js';
|
|
5
|
+
function debounce(fn, timeout) {
|
|
6
|
+
let timer;
|
|
7
|
+
return (...args) => {
|
|
8
|
+
clearTimeout(timer);
|
|
9
|
+
timer = setTimeout(fn, timeout, ...args);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export const unplugin = createUnplugin((options, meta) => {
|
|
13
|
+
const pluginName = 'unplugin-mlut';
|
|
14
|
+
const finalOptions = { output: '' };
|
|
15
|
+
const inputPath = options.input;
|
|
16
|
+
let lastCompiledCss = '';
|
|
17
|
+
const isWebpack = meta.framework === 'webpack';
|
|
18
|
+
let isVite = false;
|
|
19
|
+
let isViteWatch = false;
|
|
20
|
+
const writeCssFile = async () => {
|
|
21
|
+
const css = await jitEngine.generateCss();
|
|
22
|
+
if (lastCompiledCss !== css) {
|
|
23
|
+
lastCompiledCss = css;
|
|
24
|
+
await fs.outputFile(finalOptions.output, await transformCss(css, finalOptions)).catch((e) => logger.error('Failed to write the output file.', e));
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const debouncedWriteCssFile = debounce(writeCssFile, 500);
|
|
28
|
+
const initPlugin = async () => {
|
|
29
|
+
let inputContent = '';
|
|
30
|
+
if (inputPath) {
|
|
31
|
+
inputContent = await fs.promises.readFile(inputPath)
|
|
32
|
+
.then((r) => r.toString())
|
|
33
|
+
.catch((e) => (logger.warn('Failed to read the input file.', e), ''));
|
|
34
|
+
}
|
|
35
|
+
const cfgMatchResult = inputContent.match(/\$jit:\s*\(([^)]+)/);
|
|
36
|
+
if (cfgMatchResult != null) {
|
|
37
|
+
try {
|
|
38
|
+
const config = JSON.parse(`{${cfgMatchResult[1].replaceAll("'", '"')}}`);
|
|
39
|
+
Object.assign(finalOptions, options, config);
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
logger.error('Failed to load the JIT config from the input file.', e);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
Object.assign(finalOptions, options);
|
|
47
|
+
}
|
|
48
|
+
if (!finalOptions.output) {
|
|
49
|
+
throw new Error('Output path not specified!');
|
|
50
|
+
}
|
|
51
|
+
await jitEngine.init([inputPath, inputContent]);
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
name: pluginName,
|
|
55
|
+
async config(_config, { command }) {
|
|
56
|
+
isVite = true;
|
|
57
|
+
if (command === 'serve') {
|
|
58
|
+
isViteWatch = true;
|
|
59
|
+
}
|
|
60
|
+
await initPlugin();
|
|
61
|
+
return {
|
|
62
|
+
server: {
|
|
63
|
+
watch: {
|
|
64
|
+
ignored: ['!' + finalOptions.output]
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
configureServer(server) {
|
|
70
|
+
server.watcher.add(finalOptions.output);
|
|
71
|
+
},
|
|
72
|
+
webpack(compiler) {
|
|
73
|
+
compiler.hooks.beforeCompile.tapPromise(pluginName, initPlugin);
|
|
74
|
+
},
|
|
75
|
+
async buildStart() {
|
|
76
|
+
if (meta.framework === 'rollup') {
|
|
77
|
+
await initPlugin();
|
|
78
|
+
}
|
|
79
|
+
if (inputPath) {
|
|
80
|
+
this.addWatchFile(inputPath);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
transformInclude(id) {
|
|
84
|
+
if (isWebpack && id.endsWith('.html')) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return !(id.endsWith('.css') ||
|
|
88
|
+
id.endsWith('.scss') ||
|
|
89
|
+
id.includes('node_modules/'));
|
|
90
|
+
},
|
|
91
|
+
transform(code, id) {
|
|
92
|
+
jitEngine.putContent(id, code);
|
|
93
|
+
return null;
|
|
94
|
+
},
|
|
95
|
+
async transformIndexHtml(html, ctx) {
|
|
96
|
+
jitEngine.putContent(ctx.filename, html);
|
|
97
|
+
if (isViteWatch) {
|
|
98
|
+
debouncedWriteCssFile();
|
|
99
|
+
return {
|
|
100
|
+
html,
|
|
101
|
+
tags: [
|
|
102
|
+
{
|
|
103
|
+
tag: 'link',
|
|
104
|
+
attrs: { rel: 'stylesheet', href: finalOptions.output },
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
else if (isVite) {
|
|
110
|
+
await writeCssFile();
|
|
111
|
+
}
|
|
112
|
+
return html;
|
|
113
|
+
},
|
|
114
|
+
async watchChange(id, change) {
|
|
115
|
+
if (inputPath === id) {
|
|
116
|
+
await fs.promises.readFile(id)
|
|
117
|
+
.then((data) => jitEngine.updateSassConfig(data.toString()));
|
|
118
|
+
if (isVite) {
|
|
119
|
+
await writeCssFile();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else if (change.event === 'delete') {
|
|
123
|
+
jitEngine.deleteContent(id);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
async buildEnd() {
|
|
127
|
+
if (!isVite) {
|
|
128
|
+
await writeCssFile();
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
export const rollup = unplugin.rollup;
|
|
134
|
+
export const vite = unplugin.vite;
|
|
135
|
+
export const webpack = unplugin.webpack;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
const require = createRequire(import.meta.url);
|
|
3
|
+
let isMinifierCanAddPrefixes = false;
|
|
4
|
+
const transformByAvailableTool = await import('csso')
|
|
5
|
+
.then(({ minify }) => ((css, { noMergeMq }) => Promise.resolve(minify(css, { forceMediaMerge: !noMergeMq }).css)))
|
|
6
|
+
.catch(async () => {
|
|
7
|
+
const { transform, browserslistToTargets } = (await import('lightningcss'));
|
|
8
|
+
const browserslist = (await import('browserslist')).default;
|
|
9
|
+
let targets;
|
|
10
|
+
isMinifierCanAddPrefixes = true;
|
|
11
|
+
return (css, { autoprefixer, minify }) => {
|
|
12
|
+
if (autoprefixer && targets == undefined) {
|
|
13
|
+
targets = browserslistToTargets(browserslist());
|
|
14
|
+
}
|
|
15
|
+
const { code } = transform({
|
|
16
|
+
filename: 'style.css',
|
|
17
|
+
targets,
|
|
18
|
+
code: Buffer.from(css),
|
|
19
|
+
minify,
|
|
20
|
+
});
|
|
21
|
+
return Promise.resolve(code.toString());
|
|
22
|
+
};
|
|
23
|
+
})
|
|
24
|
+
.catch(() => {
|
|
25
|
+
const CleanCss = require('clean-css');
|
|
26
|
+
const minifier = new CleanCss({
|
|
27
|
+
level: 2,
|
|
28
|
+
returnPromise: true,
|
|
29
|
+
});
|
|
30
|
+
return (css) => minifier.minify(css).then((r) => r.styles);
|
|
31
|
+
})
|
|
32
|
+
.catch(async () => {
|
|
33
|
+
const esbuild = await import('esbuild');
|
|
34
|
+
return async (css) => {
|
|
35
|
+
return esbuild.transform(css, {
|
|
36
|
+
loader: 'css',
|
|
37
|
+
minify: true,
|
|
38
|
+
}).then((r) => r.code);
|
|
39
|
+
};
|
|
40
|
+
})
|
|
41
|
+
.catch(() => {
|
|
42
|
+
throw new Error('No CSS minifier was found. You can install one of these: csso, lightningcss, clean-css, cssnano or esbuild');
|
|
43
|
+
});
|
|
44
|
+
const applyAutoprefixer = await import('autoprefixer')
|
|
45
|
+
.then(async (autoprefixer) => {
|
|
46
|
+
const postcss = (await import('postcss')).default;
|
|
47
|
+
const processor = postcss([autoprefixer.default]);
|
|
48
|
+
const options = { from: undefined };
|
|
49
|
+
return (css) => processor.process(css, options).then((r) => (r.css));
|
|
50
|
+
})
|
|
51
|
+
.catch(() => undefined);
|
|
52
|
+
export async function transformCss(css, options) {
|
|
53
|
+
let result = css;
|
|
54
|
+
if (options.minify || isMinifierCanAddPrefixes) {
|
|
55
|
+
result = await transformByAvailableTool(css, options);
|
|
56
|
+
}
|
|
57
|
+
if (options.autoprefixer && !isMinifierCanAddPrefixes) {
|
|
58
|
+
if (applyAutoprefixer === undefined) {
|
|
59
|
+
throw new Error('The Autoprefixer package are not installed. You can do this with `npm i -D postcss autoprefixer`');
|
|
60
|
+
}
|
|
61
|
+
result = await applyAutoprefixer(result);
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mlut/plugins",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "mlut plugins for Rollup, Vite and Webpack",
|
|
5
|
+
"author": "mr150",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://mr150.github.io/mlut/",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"mlut",
|
|
12
|
+
"atomic css",
|
|
13
|
+
"functional css",
|
|
14
|
+
"utility-first",
|
|
15
|
+
"plugin",
|
|
16
|
+
"unplugin",
|
|
17
|
+
"rollup",
|
|
18
|
+
"vite",
|
|
19
|
+
"webpack"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"directory": "packages/plugins",
|
|
24
|
+
"url": "https://github.com/mr150/mlut.git"
|
|
25
|
+
},
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/mr150/mlut/issues"
|
|
28
|
+
},
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "rm -rf dist && tsc"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist"
|
|
39
|
+
],
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/clean-css": "^4.2.11",
|
|
42
|
+
"@types/fs-extra": "^11.0.4",
|
|
43
|
+
"@types/node": "^20.10.5",
|
|
44
|
+
"autoprefixer": "^10.4.19",
|
|
45
|
+
"browserslist": "^4.23.1",
|
|
46
|
+
"clean-css": "^5.3.3",
|
|
47
|
+
"csso": "^5.0.5",
|
|
48
|
+
"esbuild": "^0.21.5",
|
|
49
|
+
"lightningcss": "^1.25.1",
|
|
50
|
+
"postcss": "^8.4.38",
|
|
51
|
+
"typescript": "^4.8.0"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@mlut/core": "^2.0.0",
|
|
55
|
+
"fs-extra": "^11.2.0",
|
|
56
|
+
"unplugin": "^1.10.1"
|
|
57
|
+
}
|
|
58
|
+
}
|