Skip to content

Commit 7600d75

Browse files
committed
feat: migrate to native ESM with no build step
- Rename src/ to lib/ — source files are shipped directly, no compilation - Add "type": "module" to package.json for native ESM support (Node 18+) - Convert bin/lessc, test files, and build scripts from CJS to ESM - Rename Gruntfile.js and .eslintrc.js to .cjs (must remain CommonJS) - Add .js extensions to all relative import paths for ESM resolution - Use createRequire() for optional dependency resolution (npm packages, JSON) - Configure TypeScript for check-only mode (noEmit: true, allowJs: true) - Update Rollup config to read from lib/ directly - Update CI matrix to drop Node 16 (minimum Node 18+) - Browser build is smaller: 500KB (was 509KB), minified 153KB (was 158KB) - All 139 tests pass
1 parent 6f82d19 commit 7600d75

158 files changed

Lines changed: 6749 additions & 5744 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ jobs:
2323
node: 'lts/-1'
2424
- os: ubuntu-latest
2525
node: 'lts/-2'
26-
- os: ubuntu-latest
27-
node: 'lts/-3'
2826

2927
runs-on: ${{ matrix.os }}
3028
# This has copy/paste steps and should be refactored using DRY

packages/less/.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# project-specific
22
tmp
3-
lib
43
test/browser/less.min.js
54
test/browser/less.min.js.map
65
test/sourcemaps/**/*.map
Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -193,27 +193,16 @@ module.exports = function(grunt) {
193193
}
194194
},
195195
build: {
196-
command: [
197-
/** Browser runtime */
198-
"node build/rollup.js --dist",
199-
/** Node.js runtime */
200-
"npm run build"
201-
].join(" && ")
196+
command: "node build/rollup.js --dist"
202197
},
203198
testbuild: {
204-
command: [
205-
"npm run build",
206-
"node build/rollup.js --browser --out=./tmp/browser/less.min.js"
207-
].join(" && ")
208-
},
209-
testcjs: {
210-
command: "npm run build"
199+
command: "node build/rollup.js --browser --out=./tmp/browser/less.min.js"
211200
},
212201
testbrowser: {
213202
command: "node build/rollup.js --browser --out=./tmp/browser/less.min.js"
214203
},
215204
test: {
216-
command: 'npx ts-node test/test-es6.ts && node test/index.js'
205+
command: 'node test/test-es6.js && node test/index.js'
217206
},
218207
generatebrowser: {
219208
command: 'node test/browser/generator/generate.js'
@@ -265,11 +254,11 @@ module.exports = function(grunt) {
265254
eslint: {
266255
target: [
267256
"test/**/*.js",
268-
"src/less*/**/*.js",
257+
"lib/less*/**/*.js",
269258
"!test/less/errors/plugin/plugin-error.js"
270259
],
271260
options: {
272-
configFile: ".eslintrc.js",
261+
configFile: ".eslintrc.cjs",
273262
fix: true
274263
}
275264
},
@@ -381,9 +370,8 @@ module.exports = function(grunt) {
381370
// Run shell plugin test
382371
grunt.registerTask("shell-plugin", ["shell:plugin"]);
383372

384-
// Quickly build and run Node tests
373+
// Quickly run Node tests (no build step needed)
385374
grunt.registerTask("quicktest", [
386-
"shell:testcjs",
387375
"shell:test"
388376
]);
389377

@@ -397,7 +385,6 @@ module.exports = function(grunt) {
397385

398386
// Run benchmark
399387
grunt.registerTask("benchmark", [
400-
"shell:testcjs",
401388
"shell:benchmark"
402389
]);
403390
};

packages/less/bin/lessc

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,17 @@
22

33
/* eslint indent: [2, 2, {"SwitchCase": 1}] */
44

5-
'use strict';
5+
import path from 'path';
6+
import os from 'os';
7+
import { createRequire } from 'module';
8+
import fs from '../lib/less-node/fs.js';
9+
import * as utils from '../lib/less/utils.js';
10+
import * as Constants from '../lib/less/constants.js';
11+
import less from '../lib/less-node/index.js';
612

7-
var path = require('path');
8-
var fs = require('../lib/less-node/fs').default;
9-
var os = require('os');
10-
var utils = require('../lib/less/utils');
11-
var Constants = require('../lib/less/constants');
12-
13-
var less = require('../lib/less-node').default;
13+
const require = createRequire(import.meta.url);
1414

1515
var errno;
16-
var mkdirp;
1716

1817
try {
1918
errno = require('errno');
@@ -108,22 +107,22 @@ function render() {
108107
}
109108

110109
// Handle explicit sourceMapFullFilename (from --source-map=filename)
111-
// Normalization of other options (sourceMapBasepath, sourceMapRootpath, etc.)
110+
// Normalization of other options (sourceMapBasepath, sourceMapRootpath, etc.)
112111
// is handled automatically in parse-tree.js
113112
if (sourceMapOptions.sourceMapFullFilename && !sourceMapFileInline) {
114113
var mapFilename = path.resolve(process.cwd(), sourceMapOptions.sourceMapFullFilename);
115114
var mapDir = path.dirname(mapFilename);
116-
115+
117116
if (output) {
118117
var outputDir = path.dirname(output);
119118
// Set sourceMapOutputFilename relative to map directory
120119
sourceMapOptions.sourceMapOutputFilename = path.join(
121-
path.relative(mapDir, outputDir),
120+
path.relative(mapDir, outputDir),
122121
path.basename(output)
123122
);
124123
// Set sourceMapFilename relative to output directory (for sourceMappingURL comment)
125124
sourceMapOptions.sourceMapFilename = path.join(
126-
path.relative(outputDir, mapDir),
125+
path.relative(outputDir, mapDir),
127126
path.basename(sourceMapOptions.sourceMapFullFilename)
128127
);
129128
} else {
@@ -151,6 +150,7 @@ function render() {
151150
return;
152151
}
153152

153+
var mkdirp;
154154
var ensureDirectory = function ensureDirectory(filepath) {
155155
var dir = path.dirname(filepath);
156156
var cmd;
@@ -189,7 +189,7 @@ function render() {
189189

190190
// To fix https://github.com/less/less.js/issues/3646
191191
output = output.toString();
192-
192+
193193
fs.writeFile(filename, output, 'utf8', function (err) {
194194
if (err) {
195195
var description = 'Error: ';
@@ -404,7 +404,7 @@ function processPluginQueue() {
404404
case 'silent':
405405
options.silent = silent = true;
406406
break;
407-
407+
408408
case 'quiet':
409409
options.quiet = quiet = true;
410410
break;
@@ -539,7 +539,7 @@ function processPluginQueue() {
539539
}
540540

541541
break;
542-
542+
543543
case 'ie-compat':
544544
pendingDeprecations.push('Warning: The --ie-compat option is deprecated, as it has no effect on compilation.');
545545
break;
@@ -652,7 +652,7 @@ function processPluginQueue() {
652652
case 'disable-plugin-rule':
653653
options.disablePluginRule = true;
654654
break;
655-
655+
656656
default:
657657
queuePlugins.push({
658658
name: arg,
@@ -675,4 +675,4 @@ function processPluginQueue() {
675675
} else {
676676
render();
677677
}
678-
})();
678+
})();

packages/less/build/banner.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
import { createRequire } from 'module';
2+
3+
const require = createRequire(import.meta.url);
14
const pkg = require('./../package.json');
25

3-
module.exports =
6+
export default
47
`/**
58
* Less - ${ pkg.description } v${ pkg.version }
69
* http://lesscss.org
7-
*
10+
*
811
* Copyright (c) 2009-${new Date().getFullYear()}, ${ pkg.author.name } <${ pkg.author.email }>
912
* Licensed under the ${ pkg.license } License.
1013
*

packages/less/build/rollup.js

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
1-
const rollup = require('rollup');
2-
const typescript = require('rollup-plugin-typescript2');
3-
const commonjs = require('@rollup/plugin-commonjs');
4-
const json = require('@rollup/plugin-json');
5-
const resolve = require('@rollup/plugin-node-resolve').nodeResolve;
6-
const terser = require('rollup-plugin-terser').terser;
7-
const banner = require('./banner');
8-
const path = require('path');
1+
import { rollup } from 'rollup';
2+
import commonjs from '@rollup/plugin-commonjs';
3+
import json from '@rollup/plugin-json';
4+
import { nodeResolve as resolve } from '@rollup/plugin-node-resolve';
5+
import { terser } from 'rollup-plugin-terser';
6+
import banner from './banner.js';
7+
import path from 'path';
8+
import { fileURLToPath } from 'url';
9+
import minimist from 'minimist';
910

11+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
1012
const rootPath = path.join(__dirname, '..');
1113

12-
const args = require('minimist')(process.argv.slice(2));
14+
const args = minimist(process.argv.slice(2));
1315

1416
let outDir = args.dist ? './dist' : './tmp';
1517

1618
async function buildBrowser() {
17-
let bundle = await rollup.rollup({
18-
input: './src/less-browser/bootstrap.js',
19+
let bundle = await rollup({
20+
input: './lib/less-browser/bootstrap.js',
1921
output: [
2022
{
2123
file: 'less.js',
@@ -30,18 +32,6 @@ async function buildBrowser() {
3032
resolve(),
3133
commonjs(),
3234
json(),
33-
typescript({
34-
verbosity: 2,
35-
tsconfigDefaults: {
36-
compilerOptions: {
37-
allowJs: true,
38-
sourceMap: true,
39-
target: 'ES5'
40-
}
41-
},
42-
include: [ '*.ts', '**/*.ts', '*.js', '**/*.js' ],
43-
exclude: ['node_modules'] // only transpile our source code
44-
}),
4535
terser({
4636
compress: true,
4737
include: [/^.+\.min\.js$/],
@@ -65,7 +55,7 @@ async function buildBrowser() {
6555
format: 'umd',
6656
name: 'less',
6757
banner
68-
});
58+
});
6959
}
7060

7161
if (!args.out || args.out.indexOf('less.min.js') > -1) {

0 commit comments

Comments
 (0)