Skip to content

Commit 4ae859e

Browse files
authored
feat: modules (iam-medvedev#106)
1 parent 0db8b89 commit 4ae859e

File tree

9 files changed

+582
-262
lines changed

9 files changed

+582
-262
lines changed

__tests__/__snapshots__/index.test.ts.snap

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,7 @@ exports[`less-loader > builds successful with less as entrypoint 1`] = `
3030
"
3131
`;
3232

33-
exports[`less-loader > onResolve with watch mode 1`] = `
34-
{
35-
"path": "/path",
36-
"watchFiles": [
37-
"/path",
38-
],
39-
}
33+
exports[`less-loader > works with module.less 1`] = `
34+
".o{display:block}.o{color:#000}.button{color:gray}.foo .l{color:red}.foo .l{color:green}
35+
"
4036
`;

__tests__/index.test.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import { describe, expect, it, vi } from 'vitest';
1+
import { describe, expect, it } from 'vitest';
22
import * as path from 'path';
3-
import { build, BuildOptions, PluginBuild } from 'esbuild';
3+
import { build, BuildOptions } from 'esbuild';
44
import { lessLoader, LoaderOptions } from '../src/index';
55

66
const entryPoints = [
77
path.resolve(__dirname, '../', 'example', 'index.ts'),
88
path.resolve(__dirname, '../', 'example', 'index-custom-filter.ts'),
99
path.resolve(__dirname, '../', 'example', 'index.less'),
10+
path.resolve(__dirname, '../', 'example', 'test.module.ts'),
1011
];
1112

1213
const commonOptions: BuildOptions = {
@@ -21,20 +22,29 @@ const commonOptions: BuildOptions = {
2122
},
2223
};
2324

24-
const buildLess = async ({
25+
type BuildLessProps = {
26+
lessOptions?: Less.Options;
27+
loaderOptions?: LoaderOptions;
28+
entryPoint?: string;
29+
buildOptions?: BuildOptions;
30+
};
31+
32+
async function buildLess({
2533
lessOptions,
2634
loaderOptions,
2735
entryPoint = entryPoints[0],
28-
}: { lessOptions?: Less.Options; loaderOptions?: LoaderOptions; entryPoint?: string } = {}) => {
36+
buildOptions: _buildOptions = {},
37+
}: BuildLessProps = {}) {
2938
const buildOptions: BuildOptions = {
3039
...commonOptions,
3140
entryPoints: [entryPoint],
3241
plugins: [lessLoader(lessOptions, loaderOptions)],
42+
..._buildOptions,
3343
};
3444

3545
const { outputFiles } = await build(buildOptions);
3646
return outputFiles;
37-
};
47+
}
3848

3949
describe('less-loader', () => {
4050
it('exported module', () => {
@@ -137,4 +147,18 @@ describe('less-loader', () => {
137147
}),
138148
).rejects.toThrow();
139149
});
150+
151+
it('works with module.less', async () => {
152+
const result = await buildLess({
153+
entryPoint: entryPoints[3],
154+
buildOptions: {
155+
format: 'iife',
156+
},
157+
});
158+
159+
expect(result!.length).toEqual(2);
160+
161+
// Result has compiled .less
162+
expect(result![1].text).toMatchSnapshot();
163+
});
140164
});

example/module.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
declare module '*.module.less';

example/styles/style.module.less

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* This is a local name with the "local-css" loader
3+
* and a global name with the "global-css" loader
4+
*/
5+
.button {
6+
display: block;
7+
}
8+
9+
/* This is a local name with both loaders */
10+
:local(.button) {
11+
color: black;
12+
}
13+
14+
/* This is a global name with both loaders */
15+
:global(.button) {
16+
color: gray;
17+
}
18+
19+
/* "foo" is global and "bar" is local */
20+
:global .foo :local .bar {
21+
color: red;
22+
}
23+
24+
/* "foo" is global and "bar" is local */
25+
:global {
26+
.foo {
27+
:local {
28+
.bar {
29+
color: green;
30+
}
31+
}
32+
}
33+
}

example/test.module.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { button } from './styles/style.module.less';
2+
3+
const div = document.createElement('div');
4+
div.className = button;
5+
document.body.appendChild(div);

package.json

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
"package.json"
1616
],
1717
"scripts": {
18-
"build": "NODE_ENV=production ts-node ./scripts/build.ts",
18+
"build": "NODE_ENV=production tsx ./scripts/build.ts",
1919
"build:types": "NODE_ENV=production tsc --emitDeclarationOnly --declaration --outDir build",
20-
"dev:example-ts": "ts-node ./example/build.ts --ts",
21-
"build:example-ts": "NODE_ENV=production ts-node ./example/build.ts --ts",
22-
"dev:example-less": "ts-node ./example/build.ts --less",
23-
"build:example-less": "NODE_ENV=production ts-node ./example/build.ts --less",
20+
"dev:example-ts": "tsx ./example/build.ts --ts",
21+
"build:example-ts": "NODE_ENV=production tsx ./example/build.ts --ts",
22+
"dev:example-less": "tsx ./example/build.ts --less",
23+
"build:example-less": "NODE_ENV=production tsx ./example/build.ts --less",
2424
"commit": "yarn git-cz",
2525
"prepare": "husky install",
2626
"types": "tsc --noEmit",
@@ -44,29 +44,29 @@
4444
},
4545
"devDependencies": {
4646
"@semantic-release/changelog": "6.0.3",
47-
"@semantic-release/commit-analyzer": "10.0.1",
48-
"@semantic-release/github": "9.0.4",
49-
"@semantic-release/npm": "10.0.4",
50-
"@semantic-release/release-notes-generator": "11.0.4",
47+
"@semantic-release/commit-analyzer": "10.0.4",
48+
"@semantic-release/github": "9.0.6",
49+
"@semantic-release/npm": "10.0.6",
50+
"@semantic-release/release-notes-generator": "11.0.7",
5151
"@types/node": "^20.5.0",
52-
"@vitest/coverage-v8": "^0.34.2",
52+
"@vitest/coverage-v8": "^0.34.4",
5353
"cz-conventional-changelog": "3.3.0",
54-
"esbuild": "^0.19.2",
54+
"esbuild": "^0.19.3",
5555
"git-cz": "4.9.0",
5656
"husky": "^8.0.3",
57-
"lint-staged": "14.0.0",
58-
"prettier": "3.0.2",
59-
"semantic-release": "21.0.7",
60-
"ts-node": "10.9.1",
61-
"typescript": "5.1.6",
57+
"lint-staged": "14.0.1",
58+
"prettier": "3.0.3",
59+
"semantic-release": "21.1.1",
60+
"tsx": "3.12.10",
61+
"typescript": "5.2.2",
6262
"vite": "^4.4.9",
63-
"vitest": "^0.34.2"
63+
"vitest": "^0.34.4"
6464
},
6565
"peerDependencies": {
6666
"esbuild": "^0.14.x || ^0.15.0 || ^0.16.0 || ^0.17.0 || ^0.18.0 || ^0.19.0"
6767
},
6868
"dependencies": {
69-
"@types/less": "^3.0.3",
69+
"@types/less": "^3.0.4",
7070
"less": "^4.2.0"
7171
},
7272
"keywords": [

src/index.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import type { Loader, Plugin } from 'esbuild';
12
import path from 'path';
23
import { promises as fs } from 'fs';
3-
import { Plugin } from 'esbuild';
44
import less from 'less';
55
import { convertLessError, getLessImports } from './less-utils';
66

@@ -36,8 +36,9 @@ export const lessLoader = (options: Less.Options = {}, loaderOptions: LoaderOpti
3636
build.onLoad({ filter: filter || /\.less$/, namespace: 'file' }, async (args) => {
3737
const content = await fs.readFile(args.path, 'utf-8');
3838
const dir = path.dirname(args.path);
39-
40-
const isModule = path.basename(args.path).endsWith('.module.less');
39+
const basename = path.basename(args.path);
40+
const isModule = basename.endsWith('.module.less');
41+
const loader: Loader = isModule ? 'local-css' : 'css';
4142

4243
const opts: Less.Options = {
4344
filename: args.path,
@@ -51,7 +52,7 @@ export const lessLoader = (options: Less.Options = {}, loaderOptions: LoaderOpti
5152

5253
return {
5354
contents: result.css,
54-
loader: isModule ? 'local-css' : 'css',
55+
loader,
5556
resolveDir: dir,
5657
};
5758
} catch (e) {

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
"module": "commonjs",
66
"target": "es2017"
77
},
8-
"include": ["src"],
8+
"include": ["src", "example"],
99
"exclude": ["node_modules", "build"]
1010
}

0 commit comments

Comments
 (0)