@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 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
@@ -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,6 @@
1
+ export interface TransformCssOptions {
2
+ readonly minify?: boolean;
3
+ readonly autoprefixer?: boolean;
4
+ readonly noMergeMq?: boolean;
5
+ }
6
+ export declare function transformCss(css: string, options: TransformCssOptions): Promise<string>;
@@ -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
+ }