feat: deprecation warnings for Less + monorepo chores (#4319)

* Added deprecation warnings

* Remove warning about combinator

* Switch to PNPM

* Update local dependencies

* Remove inner dist folder

* Create symbolic link to dist folder

* Flip dist folders

* Create symbolic link in root

* Add contributors

* 👥 Add @matthew-dean as a contributor

* 👥 Add @cloudhead as a contributor

* 👥 Add @lukeapage as a contributor

* 👥 Add @seven-phases-max as a contributor

* 👥 Add @iChenLei as a contributor

* 👥 Add @puckowski as a contributor

* Add more contributors

* Finish deprecation warnings

* Resolve deletions and such

* Fix symbolic link

* Update ci.yml to use PNPM

* Update ci.yml to use PNPM

* Refine CI versions

* Fix node printed version

* Better fix for #4258 and #4292

* Re-enable other tests
This commit is contained in:
Matthew Dean
2025-03-01 11:23:48 -08:00
committed by GitHub
parent 1e7b0038d4
commit d1abdabcc2
35 changed files with 9849 additions and 45330 deletions

77
.all-contributorsrc Normal file
View File

@@ -0,0 +1,77 @@
{
"projectName": "Less.js",
"projectOwner": "The Less CSS Team",
"repoType": "github",
"repoHost": "https://github.com/less/less.js",
"files": [
"README.md"
],
"imageSize": 100,
"commit": true,
"commitConvention": "gitmoji",
"contributors": [
{
"login": "matthew-dean",
"name": "Matthew Dean",
"avatar_url": "https://avatars.githubusercontent.com/u/414752?v=4",
"profile": "https://github.com/matthew-dean",
"contributions": [
"code",
"doc",
"maintenance",
"projectManagement"
]
},
{
"login": "cloudhead",
"name": "Alexis Sellier",
"avatar_url": "https://avatars.githubusercontent.com/u/40774?v=4",
"profile": "https://cloudhead.io/",
"contributions": [
"code",
"doc"
]
},
{
"login": "lukeapage",
"name": "Luke Page",
"avatar_url": "https://avatars.githubusercontent.com/u/309321?v=4",
"profile": "https://github.com/lukeapage",
"contributions": [
"code"
]
},
{
"login": "seven-phases-max",
"name": "Max Mikhailov",
"avatar_url": "https://avatars.githubusercontent.com/u/5304376?v=4",
"profile": "https://github.com/seven-phases-max",
"contributions": [
"code"
]
},
{
"login": "iChenLei",
"name": "Lei Chen",
"avatar_url": "https://avatars.githubusercontent.com/u/14012511?v=4",
"profile": "https://github.com/iChenLei",
"contributions": [
"code",
"bug",
"doc"
]
},
{
"login": "puckowski",
"name": "Daniel Puckowski",
"avatar_url": "https://avatars.githubusercontent.com/u/3059609?v=4",
"profile": "https://github.com/puckowski",
"contributions": [
"code",
"bug"
]
}
],
"contributorsPerLine": 7,
"linkToUsage": true
}

View File

@@ -9,86 +9,40 @@ on:
branches: [main, master]
jobs:
basic_node_test:
name: 'Basic tests on ubuntu-latest with nodejs v22 (current LTS version)'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node }}
- name: Install npm dependencies
run: npm install
- name: Print put node & npm version
run: node --version && npm --version
- name: Install chromium
run: npx playwright install chromium
- name: Run unit test
run: npm run test
windows_and_macos_test:
name: 'Platform tests on ${{matrix.os}} with nodejs v${{matrix.node}}'
needs: basic_node_test
test:
name: 'Tests on ${{matrix.os}} with Node "${{matrix.node}}"'
strategy:
matrix:
# Test all mainstream operating system
os: [macos-latest, windows-latest]
node: [22]
runs-on: ${{ matrix.os }}
steps:
# Pull repo to test machine
- uses: actions/checkout@v2
# Configures the node version used on GitHub-hosted runners
- uses: actions/setup-node@v2
with:
# The Node.js version to configure
node-version: ${{ matrix.node }}
- name: Install npm dependencies
run: npm install
- name: Print put node & npm version
# Output useful info for debugging.
run: node --version && npm --version
- name: Install chromium
run: npx playwright install chromium
- name: Run unit test
run: npm run test
# Test all mainstream operating systems
os: [ubuntu-latest, macos-latest, windows-latest]
node: ['current']
include:
- os: ubuntu-latest
node: 'lts/*'
- os: ubuntu-latest
node: 'lts/-1'
- os: ubuntu-latest
node: 'lts/-2'
- os: ubuntu-latest
node: 'lts/-3'
historical_versions_node_test:
name: 'Historical version nodejs v${{matrix.node}} test'
needs: basic_node_test
strategy:
matrix:
os: [ubuntu-latest]
node: [14, 16, 18, 20]
runs-on: ${{ matrix.os }}
# This has copy/paste steps and should be refactored using DRY
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node }}
- name: Install npm dependencies
run: npm install
- name: Print put node & npm version
run: node --version && npm --version
- name: Install chromium
run: npx playwright install chromium
- name: Run unit test
run: npm run test
latest_nodejs_testing_node23:
name: 'Latest nodejs v23 test'
needs: basic_node_test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 23
- name: Install npm dependencies
run: npm install
- name: Print put node & npm version
run: node --version && npm --version
- name: Install chromium
run: npx playwright install chromium
- name: Run unit test
run: npm run test
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Print put node & npm version
run: node --version && pnpm --version
- name: Install chromium
run: npx playwright install chromium
- name: Run unit test
run: pnpm run test

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
v16

View File

@@ -25,7 +25,7 @@ Please report documentation issues in [the documentation project](https://github
* Please search for existing feature requests first to see if something similar already exists.
* Include a clear and specific use-case. We love new ideas, but we do not add language features without a reason.
* Consider whether or not your language feature would be better as a function or implemented in a 3rd-party build system such as [assemble-less](http://github.com/assemble/assemble-less).
* Consider whether or not your language feature would be better as a function or implemented in a 3rd-party build system
## Pull Requests
@@ -51,15 +51,4 @@ Learn more about [developing Less.js](http://lesscss.org/usage/#developing-less)
## Releases
Releases are managed using Lerna. First, we determine if the release is a major (breaking), minor (new features), or patch (bug fix) change.
Then, you can run Lerna with a command like:
```
npx lerna version minor
```
To publish, run the Lerna command for publishing without incrementing the version, as in:
```
npx lerna publish from-package --no-private
```
Releases are managed using PNPM. Instructions TBD

View File

@@ -1,4 +1,7 @@
<p align="center"><img src="http://lesscss.org/public/img/less_logo.png" width="264" height="117">
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
<p align="center">
<a href="https://github.com/less/less.js/actions?query=branch%3Amaster"><img src="https://github.com/less/less.js/actions/workflows/ci.yml/badge.svg?branch=master" alt="Github Actions CI"/></a>
@@ -39,11 +42,39 @@ See the [changelog](CHANGELOG.md)
## Contributors
### Code Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
<a href="https://github.com/less/less.js/graphs/contributors"><img src="https://opencollective.com/less/contributors.svg?width=890&button=false" /></a>
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tbody>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/matthew-dean"><img src="https://avatars.githubusercontent.com/u/414752?v=4?s=100" width="100px;" alt="Matthew Dean"/><br /><sub><b>Matthew Dean</b></sub></a><br /><a href="https://github.com/The Less CSS Team/Less.js/commits?author=matthew-dean" title="Code">💻</a> <a href="https://github.com/The Less CSS Team/Less.js/commits?author=matthew-dean" title="Documentation">📖</a> <a href="#maintenance-matthew-dean" title="Maintenance">🚧</a> <a href="#projectManagement-matthew-dean" title="Project Management">📆</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://cloudhead.io/"><img src="https://avatars.githubusercontent.com/u/40774?v=4?s=100" width="100px;" alt="Alexis Sellier"/><br /><sub><b>Alexis Sellier</b></sub></a><br /><a href="https://github.com/The Less CSS Team/Less.js/commits?author=cloudhead" title="Code">💻</a> <a href="https://github.com/The Less CSS Team/Less.js/commits?author=cloudhead" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lukeapage"><img src="https://avatars.githubusercontent.com/u/309321?v=4?s=100" width="100px;" alt="Luke Page"/><br /><sub><b>Luke Page</b></sub></a><br /><a href="https://github.com/The Less CSS Team/Less.js/commits?author=lukeapage" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/seven-phases-max"><img src="https://avatars.githubusercontent.com/u/5304376?v=4?s=100" width="100px;" alt="Max Mikhailov"/><br /><sub><b>Max Mikhailov</b></sub></a><br /><a href="https://github.com/The Less CSS Team/Less.js/commits?author=seven-phases-max" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/iChenLei"><img src="https://avatars.githubusercontent.com/u/14012511?v=4?s=100" width="100px;" alt="Lei Chen"/><br /><sub><b>Lei Chen</b></sub></a><br /><a href="https://github.com/The Less CSS Team/Less.js/commits?author=iChenLei" title="Code">💻</a> <a href="https://github.com/The Less CSS Team/Less.js/issues?q=author%3AiChenLei" title="Bug reports">🐛</a> <a href="https://github.com/The Less CSS Team/Less.js/commits?author=iChenLei" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/puckowski"><img src="https://avatars.githubusercontent.com/u/3059609?v=4?s=100" width="100px;" alt="Daniel Puckowski"/><br /><sub><b>Daniel Puckowski</b></sub></a><br /><a href="https://github.com/The Less CSS Team/Less.js/commits?author=puckowski" title="Code">💻</a> <a href="https://github.com/The Less CSS Team/Less.js/issues?q=author%3Apuckowski" title="Bug reports">🐛</a></td>
</tr>
</tbody>
<tfoot>
<tr>
<td align="center" size="13px" colspan="7">
<img src="https://raw.githubusercontent.com/all-contributors/all-contributors-cli/1b8533af435da9854653492b1327a23a4dbd0a10/assets/logo-small.svg">
<a href="https://all-contributors.js.org/docs/en/bot/usage">Add your contributions</a>
</img>
</td>
</tr>
</tfoot>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
## [License](LICENSE)

225
dist/less.js vendored
View File

@@ -962,47 +962,6 @@
ALL: 2
};
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __spreadArray(to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
/**
* Returns the object type of the given payload
*
@@ -1034,7 +993,7 @@
}
function assignProp(carry, key, newVal, originalObject, includeNonenumerable) {
var propType = {}.propertyIsEnumerable.call(originalObject, key)
const propType = {}.propertyIsEnumerable.call(originalObject, key)
? 'enumerable'
: 'nonenumerable';
if (propType === 'enumerable')
@@ -1058,22 +1017,21 @@
* @returns {T} the target with replaced values
* @export
*/
function copy(target, options) {
if (options === void 0) { options = {}; }
function copy(target, options = {}) {
if (isArray(target)) {
return target.map(function (item) { return copy(item, options); });
return target.map((item) => copy(item, options));
}
if (!isPlainObject(target)) {
return target;
}
var props = Object.getOwnPropertyNames(target);
var symbols = Object.getOwnPropertySymbols(target);
return __spreadArray(__spreadArray([], props, true), symbols, true).reduce(function (carry, key) {
const props = Object.getOwnPropertyNames(target);
const symbols = Object.getOwnPropertySymbols(target);
return [...props, ...symbols].reduce((carry, key) => {
if (isArray(options.props) && !options.props.includes(key)) {
return carry;
}
var val = target[key];
var newVal = copy(val, options);
const val = target[key];
const newVal = copy(val, options);
assignProp(carry, key, newVal, target, options.nonenumerable);
return carry;
}, {});
@@ -1299,20 +1257,24 @@
* @returns {string}
*/
LessError.prototype.toString = function (options) {
var _a;
options = options || {};
var isWarning = ((_a = this.type) !== null && _a !== void 0 ? _a : '').toLowerCase().includes('warning');
var type = isWarning ? this.type : "".concat(this.type, "Error");
var color = isWarning ? 'yellow' : 'red';
var message = '';
var extract = this.extract || [];
var error = [];
var stylize = function (str) { return str; };
if (options.stylize) {
var type = typeof options.stylize;
if (type !== 'function') {
throw Error("options.stylize should be a function, got a ".concat(type, "!"));
var type_1 = typeof options.stylize;
if (type_1 !== 'function') {
throw Error("options.stylize should be a function, got a ".concat(type_1, "!"));
}
stylize = options.stylize;
}
if (this.line !== null) {
if (typeof extract[0] === 'string') {
if (!isWarning && typeof extract[0] === 'string') {
error.push(stylize("".concat(this.line - 1, " ").concat(extract[0]), 'grey'));
}
if (typeof extract[1] === 'string') {
@@ -1324,21 +1286,21 @@
}
error.push(errorTxt);
}
if (typeof extract[2] === 'string') {
if (!isWarning && typeof extract[2] === 'string') {
error.push(stylize("".concat(this.line + 1, " ").concat(extract[2]), 'grey'));
}
error = "".concat(error.join('\n') + stylize('', 'reset'), "\n");
}
message += stylize("".concat(this.type, "Error: ").concat(this.message), 'red');
message += stylize("".concat(type, ": ").concat(this.message), color);
if (this.filename) {
message += stylize(' in ', 'red') + this.filename;
message += stylize(' in ', color) + this.filename;
}
if (this.line) {
message += stylize(" on line ".concat(this.line, ", column ").concat(this.column + 1, ":"), 'grey');
}
message += "\n".concat(error);
if (this.callLine) {
message += "".concat(stylize('from ', 'red') + (this.filename || ''), "/n");
message += "".concat(stylize('from ', color) + (this.filename || ''), "/n");
message += "".concat(stylize(this.callLine, 'grey'), " ").concat(this.callExtract, "/n");
}
return message;
@@ -1519,7 +1481,8 @@
// context
'processImports',
// Used by the import manager to stop multiple import visitors being created.
'pluginManager' // Used as the plugin manager for the session
'pluginManager',
'quiet', // option - whether to log warnings
];
contexts.Parse = function (options) {
copyFromOriginal(options, this, parseCopyProperties);
@@ -2006,7 +1969,12 @@
catch (_) { }
if (!indices["".concat(extend.index, " ").concat(selector)]) {
indices["".concat(extend.index, " ").concat(selector)] = true;
logger$1.warn("extend '".concat(selector, "' has no matches"));
/**
* @todo Shouldn't this be an error? To alert the developer
* that they may have made an error in the selector they are
* targeting?
*/
logger$1.warn("WARNING: extend '".concat(selector, "' has no matches"));
}
});
};
@@ -3310,6 +3278,22 @@
message: msg
}, imports);
}
/**
*
* @param {string} msg
* @param {number} index
* @param {string} type
*/
function warn(msg, index, type) {
if (!context.quiet) {
logger$1.warn((new LessError({
index: index !== null && index !== void 0 ? index : parserInput.i,
filename: fileInfo.filename,
type: type ? "".concat(type.toUpperCase(), " WARNING") : 'WARNING',
message: msg
}, imports)).toString());
}
}
function expect(arg, msg) {
// some older browsers return typeof 'function' for RegExp
var result = (arg instanceof Function) ? arg.call(parsers) : parserInput.$re(arg);
@@ -4017,11 +4001,20 @@
do {
option = null;
elements = null;
var first = true;
while (!(option = parserInput.$re(/^(all)(?=\s*(\)|,))/))) {
e = this.element();
if (!e) {
break;
}
/**
* @note - This will not catch selectors in pseudos like :is() and :where() because
* they don't currently parse their contents as selectors.
*/
if (!first && e.combinator.value) {
warn('Targeting complex selectors can have unexpected behavior, and this behavior may change in the future.', index);
}
first = false;
if (elements) {
elements.push(e);
}
@@ -4081,16 +4074,23 @@
var elements;
var args;
var hasParens;
var parensIndex;
var parensWS = false;
if (s !== '.' && s !== '#') {
return;
}
parserInput.save(); // stop us absorbing part of an invalid selector
elements = this.elements();
if (elements) {
parensIndex = parserInput.i;
if (parserInput.$char('(')) {
parensWS = parserInput.isWhitespace(-2);
args = this.args(true).args;
expectChar(')');
hasParens = true;
if (parensWS) {
warn('Whitespace between a mixin name and parentheses for a mixin call is deprecated', parensIndex, 'DEPRECATED');
}
}
if (getLookup !== false) {
lookups = this.ruleLookups();
@@ -4114,6 +4114,9 @@
return new tree.NamespaceValue(mixin, lookups);
}
else {
if (!hasParens) {
warn('Calling a mixin without parentheses is deprecated', parensIndex, 'DEPRECATED');
}
return mixin;
}
}
@@ -4403,24 +4406,25 @@
expectChar(')');
return new tree.Quoted('', "alpha(opacity=".concat(value, ")"));
},
//
// A Selector Element
//
// div
// + h1
// #socks
// input[type="text"]
//
// Elements are the building blocks for Selectors,
// they are made out of a `Combinator` (see combinator rule),
// and an element name, such as a tag a class, or `*`.
//
/**
* A Selector Element
*
* div
* + h1
* #socks
* input[type="text"]
*
* Elements are the building blocks for Selectors,
* they are made out of a `Combinator` (see combinator rule),
* and an element name, such as a tag a class, or `*`.
*/
element: function () {
var e;
var c;
var v;
var index = parserInput.i;
c = this.combinator();
/** This selector parser is quite simplistic and will pass a number of invalid selectors. */
e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) ||
// eslint-disable-next-line no-control-regex
parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
@@ -5237,7 +5241,14 @@
break;
}
parserInput.save();
op = parserInput.$char('/') || parserInput.$char('*') || parserInput.$str('./');
op = parserInput.$char('/') || parserInput.$char('*');
if (!op) {
var index = parserInput.i;
op = parserInput.$str('./');
if (op) {
warn('./ operator is deprecated', index, 'DEPRECATED');
}
}
if (!op) {
parserInput.forget();
break;
@@ -7113,6 +7124,47 @@
}
});
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __spreadArray(to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
var Expression = function (value, noSpacing) {
this.value = value;
this.noSpacing = noSpacing;
@@ -10855,19 +10907,22 @@
var version = "4.2.2";
function parseNodeVersion(version) {
var match = version.match(/^v(\d{1,2})\.(\d{1,2})\.(\d{1,2})(?:-([0-9A-Za-z-.]+))?(?:\+([0-9A-Za-z-.]+))?$/); // eslint-disable-line max-len
if (!match) {
throw new Error('Unable to parse: ' + version);
}
var res = {
major: parseInt(match[1], 10),
minor: parseInt(match[2], 10),
patch: parseInt(match[3], 10),
pre: match[4] || '',
build: match[5] || '',
};
return res;
var match = version.match(/^v(\d{1,2})\.(\d{1,2})\.(\d{1,2})(?:-([0-9A-Za-z-.]+))?(?:\+([0-9A-Za-z-.]+))?$/); // eslint-disable-line max-len
if (!match) {
throw new Error('Unable to parse: ' + version);
}
var res = {
major: parseInt(match[1], 10),
minor: parseInt(match[2], 10),
patch: parseInt(match[3], 10),
pre: match[4] || '',
build: match[5] || '',
};
return res;
}
var parseNodeVersion_1 = parseNodeVersion;
function lessRoot (environment, fileManagers) {

2
dist/less.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +0,0 @@
{
"packages": [
"packages/*"
],
"useNx": false,
"npmClient": "npm",
"version": "4.2.2"
}

19651
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,12 +8,9 @@
"publish": "lerna publish from-package --no-private",
"changelog": "github-changes -o less -r less.js -a --only-pulls --use-commit-body -m \"(YYYY-MM-DD)\"",
"test": "cd packages/less && npm test",
"postinstall": "lerna bootstrap"
},
"author": {
"name": "Alexis Sellier",
"email": "self@cloudhead.net"
"postinstall": "npx only-allow pnpm"
},
"author": "Alexis Sellier <self@cloudhead.net>",
"contributors": [
"The Core Less Team"
],
@@ -26,6 +23,7 @@
"url": "https://github.com/less/less.js.git"
},
"devDependencies": {
"all-contributors-cli": "~6.26.1",
"github-changes": "^1.1.2",
"lerna": "^3.22.1",
"npm-run-all": "^4.1.5"

View File

@@ -197,8 +197,6 @@ module.exports = function(grunt) {
command: [
/** Browser runtime */
"node build/rollup.js --dist",
/** Copy to repo root */
"npm run copy:root",
/** Node.js runtime */
"npm run build"
].join(" && ")

View File

@@ -2,7 +2,7 @@
/* eslint indent: [2, 2, {"SwitchCase": 1}] */
"use strict";
'use strict';
var path = require('path');
var fs = require('../lib/less-node/fs').default;
@@ -27,6 +27,7 @@ var plugins = [];
var queuePlugins = [];
var args = process.argv.slice(1);
var silent = false;
var quiet = false;
var verbose = false;
var options = less.options;
options.plugins = plugins;
@@ -36,7 +37,7 @@ var continueProcessing = true;
var checkArgFunc = function checkArgFunc(arg, option) {
if (!option) {
console.error("".concat(arg, " option requires a parameter"));
console.error(''.concat(arg, ' option requires a parameter'));
continueProcessing = false;
process.exitCode = 1;
return false;
@@ -49,7 +50,7 @@ var checkBooleanArg = function checkBooleanArg(arg) {
var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg);
if (!onOff) {
console.error(" unable to parse ".concat(arg, " as a boolean. use one of on/t/true/y/yes/off/f/false/n/no"));
console.error(' unable to parse '.concat(arg, ' as a boolean. use one of on/t/true/y/yes/off/f/false/n/no'));
continueProcessing = false;
process.exitCode = 1;
return false;
@@ -110,7 +111,7 @@ function render() {
if (output) {
sourceMapOptions.sourceMapOutputFilename = path.basename(output);
sourceMapOptions.sourceMapFullFilename = "".concat(output, ".map");
sourceMapOptions.sourceMapFullFilename = ''.concat(output, '.map');
} // its in the same directory, so always just the basename
@@ -180,7 +181,7 @@ function render() {
return;
}
process.stdout.write("".concat(outputbase, ": "));
process.stdout.write(''.concat(outputbase, ': '));
}
if (!sourceMapFileInline) {
@@ -200,14 +201,14 @@ function render() {
if (errno && errno.errno[err.errno]) {
description += errno.errno[err.errno].description;
} else {
description += "".concat(err.code, " ").concat(err.message);
description += ''.concat(err.code, ' ').concat(err.message);
}
console.error("lessc: failed to create file ".concat(filename));
console.error('lessc: failed to create file '.concat(filename));
console.error(description);
process.exitCode = 1;
} else {
less.logger.info("lessc: wrote ".concat(filename));
less.logger.info('lessc: wrote '.concat(filename));
}
onDone();
@@ -238,14 +239,14 @@ function render() {
if (errno && errno.errno[err.errno]) {
description += errno.errno[err.errno].description;
} else {
description += "".concat(err.code, " ").concat(err.message);
description += ''.concat(err.code, ' ').concat(err.message);
}
console.error("lessc: failed to create file ".concat(output));
console.error('lessc: failed to create file '.concat(output));
console.error(description);
process.exitCode = 1;
} else {
less.logger.info("lessc: wrote ".concat(output));
less.logger.info('lessc: wrote '.concat(output));
onSuccess();
}
@@ -261,7 +262,7 @@ function render() {
var depends = '';
for (var i = 0; i < result.imports.length; i++) {
depends += "".concat(result.imports[i], " ");
depends += ''.concat(result.imports[i], ' ');
}
console.log(depends);
@@ -270,7 +271,7 @@ function render() {
var parseLessFile = function parseLessFile(e, data) {
if (e) {
console.error("lessc: ".concat(e.message));
console.error('lessc: '.concat(e.message));
process.exitCode = 1;
return;
}
@@ -297,12 +298,14 @@ function render() {
},
warn: function warn(msg) {
// do not show warning if the silent option is used
if (!silent) {
if (!silent && !quiet) {
console.warn(msg);
}
},
error: function error(msg) {
console.error(msg);
if (!silent) {
console.error(msg);
}
}
});
@@ -344,7 +347,7 @@ function processPluginQueue() {
var x = 0;
function pluginError(name) {
console.error("Unable to load plugin ".concat(name, " please make sure that it is installed under or at the same level as less"));
console.error('Unable to load plugin '.concat(name, ' please make sure that it is installed under or at the same level as less'));
process.exitCode = 1;
}
@@ -393,17 +396,21 @@ function processPluginQueue() {
switch (arg) {
case 'v':
case 'version':
console.log("lessc ".concat(less.version.join('.'), " (Less Compiler) [JavaScript]"));
console.log('lessc '.concat(less.version.join('.'), ' (Less Compiler) [JavaScript]'));
continueProcessing = false;
break;
case 'verbose':
verbose = true;
options.verbose = verbose = true;
break;
case 's':
case 'silent':
silent = true;
options.silent = silent = true;
break;
case 'quiet':
options.quiet = quiet = true;
break;
case 'l':
@@ -552,7 +559,7 @@ function processPluginQueue() {
} else if (m === 'all') {
options.rewriteUrls = Constants.RewriteUrls.ALL;
} else {
console.error("Unknown rewrite-urls argument ".concat(m));
console.error('Unknown rewrite-urls argument '.concat(m));
continueProcessing = false;
process.exitCode = 1;
}
@@ -575,8 +582,8 @@ function processPluginQueue() {
break;
case 'm':
case 'math':
var m = match[2];
case 'math': {
let m = match[2];
if (checkArgFunc(arg, m)) {
if (m === 'always') {
console.warn('--math=always is deprecated and will be removed in the future.');
@@ -592,7 +599,7 @@ function processPluginQueue() {
}
break;
}
case 'su':
case 'strict-units':
if (checkArgFunc(arg, match[2])) {

1
packages/less/dist Symbolic link
View File

@@ -0,0 +1 @@
../../dist

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -32,7 +32,7 @@
},
"browser": "./dist/less.js",
"engines": {
"node": ">=6"
"node": ">=14"
},
"scripts": {
"test": "grunt test",
@@ -42,7 +42,6 @@
"build": "npm-run-all clean compile",
"clean": "shx rm -rf ./lib tsconfig.tsbuildinfo",
"compile": "tsc -p tsconfig.build.json",
"copy:root": "shx cp -rf ./dist ../../",
"dev": "tsc -p tsconfig.build.json -w",
"prepublishOnly": "grunt dist"
},
@@ -56,8 +55,8 @@
"source-map": "~0.6.0"
},
"devDependencies": {
"@less/test-data": "^4.2.2",
"@less/test-import-module": "^4.0.0",
"@less/test-data": "workspace:*",
"@less/test-import-module": "workspace:*",
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^11.0.0",

View File

@@ -33,6 +33,7 @@ const lessc_helper = {
console.log(' --js Enables inline JavaScript in less files');
console.log(' -l, --lint Syntax check only (lint).');
console.log(' -s, --silent Suppresses output of error messages.');
console.log(' --quiet Suppresses output of warnings.');
console.log(' --strict-imports Forces evaluation of imports.');
console.log(' --insecure Allows imports from insecure https hosts.');
console.log(' -v, --version Prints version number and exit.');

View File

@@ -31,7 +31,8 @@ const parseCopyProperties = [
// context
'processImports', // option & context - whether to process imports. if false then imports will not be imported.
// Used by the import manager to stop multiple import visitors being created.
'pluginManager' // Used as the plugin manager for the session
'pluginManager', // Used as the plugin manager for the session
'quiet', // option - whether to log warnings
];
contexts.Parse = function(options) {

View File

@@ -106,6 +106,9 @@ LessError.prototype.constructor = LessError;
*/
LessError.prototype.toString = function(options) {
options = options || {};
const isWarning = (this.type ?? '').toLowerCase().includes('warning');
const type = isWarning ? this.type : `${this.type}Error`;
const color = isWarning ? 'yellow' : 'red';
let message = '';
const extract = this.extract || [];
@@ -120,7 +123,7 @@ LessError.prototype.toString = function(options) {
}
if (this.line !== null) {
if (typeof extract[0] === 'string') {
if (!isWarning && typeof extract[0] === 'string') {
error.push(stylize(`${this.line - 1} ${extract[0]}`, 'grey'));
}
@@ -134,15 +137,15 @@ LessError.prototype.toString = function(options) {
error.push(errorTxt);
}
if (typeof extract[2] === 'string') {
if (!isWarning && typeof extract[2] === 'string') {
error.push(stylize(`${this.line + 1} ${extract[2]}`, 'grey'));
}
error = `${error.join('\n') + stylize('', 'reset')}\n`;
}
message += stylize(`${this.type}Error: ${this.message}`, 'red');
message += stylize(`${type}: ${this.message}`, color);
if (this.filename) {
message += stylize(' in ', 'red') + this.filename;
message += stylize(' in ', color) + this.filename;
}
if (this.line) {
message += stylize(` on line ${this.line}, column ${this.column + 1}:`, 'grey');
@@ -151,7 +154,7 @@ LessError.prototype.toString = function(options) {
message += `\n${error}`;
if (this.callLine) {
message += `${stylize('from ', 'red') + (this.filename || '')}/n`;
message += `${stylize('from ', color) + (this.filename || '')}/n`;
message += `${stylize(this.callLine, 'grey')} ${this.callExtract}/n`;
}

View File

@@ -5,6 +5,7 @@ import getParserInput from './parser-input';
import * as utils from '../utils';
import functionRegistry from '../functions/function-registry';
import { ContainerSyntaxOptions, MediaSyntaxOptions } from '../tree/atrule-syntax';
import logger from '../logger';
import Selector from '../tree/selector';
import Anonymous from '../tree/anonymous';
@@ -58,6 +59,28 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
);
}
/**
*
* @param {string} msg
* @param {number} index
* @param {string} type
*/
function warn(msg, index, type) {
if (!context.quiet) {
logger.warn(
(new LessError(
{
index: index ?? parserInput.i,
filename: fileInfo.filename,
type: type ? `${type.toUpperCase()} WARNING` : 'WARNING',
message: msg
},
imports
)).toString()
);
}
}
function expect(arg, msg) {
// some older browsers return typeof 'function' for RegExp
const result = (arg instanceof Function) ? arg.call(parsers) : parserInput.$re(arg);
@@ -858,11 +881,22 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
do {
option = null;
elements = null;
while (!(option = parserInput.$re(/^(all)(?=\s*(\)|,))/))) {
let first = true;
while (!(option = parserInput.$re(/^(!?all)(?=\s*(\)|,))/))) {
e = this.element();
if (!e) {
break;
}
/**
* @note - This will not catch selectors in pseudos like :is() and :where() because
* they don't currently parse their contents as selectors.
*/
if (!first && e.combinator.value) {
warn('Targeting complex selectors can have unexpected behavior, and this behavior may change in the future.', index)
}
first = false;
if (elements) {
elements.push(e);
} else {
@@ -926,6 +960,8 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
let elements;
let args;
let hasParens;
let parensIndex;
let parensWS = false;
if (s !== '.' && s !== '#') { return; }
@@ -934,10 +970,15 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
elements = this.elements();
if (elements) {
parensIndex = parserInput.i;
if (parserInput.$char('(')) {
parensWS = parserInput.isWhitespace(-2);
args = this.args(true).args;
expectChar(')');
hasParens = true;
if (parensWS) {
warn('Whitespace between a mixin name and parentheses for a mixin call is deprecated', parensIndex, 'DEPRECATED');
}
}
if (getLookup !== false) {
@@ -965,6 +1006,9 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
return new tree.NamespaceValue(mixin, lookups);
}
else {
if (!hasParens) {
warn('Calling a mixin without parentheses is deprecated', parensIndex, 'DEPRECATED');
}
return mixin;
}
}
@@ -1284,18 +1328,18 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
return new tree.Quoted('', `alpha(opacity=${value})`);
},
//
// A Selector Element
//
// div
// + h1
// #socks
// input[type="text"]
//
// Elements are the building blocks for Selectors,
// they are made out of a `Combinator` (see combinator rule),
// and an element name, such as a tag a class, or `*`.
//
/**
* A Selector Element
*
* div
* + h1
* #socks
* input[type="text"]
*
* Elements are the building blocks for Selectors,
* they are made out of a `Combinator` (see combinator rule),
* and an element name, such as a tag a class, or `*`.
*/
element: function () {
let e;
let c;
@@ -1304,6 +1348,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
c = this.combinator();
/** This selector parser is quite simplistic and will pass a number of invalid selectors. */
e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) ||
// eslint-disable-next-line no-control-regex
parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
@@ -1601,7 +1646,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
if (parserInput.$char(';')) {
value = new Anonymous('');
} else {
value = this.permissiveValue(/[;}]/);
value = this.permissiveValue(/[;}]/, true);
}
}
// Try to store values as anonymous
@@ -1622,7 +1667,12 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
if (value) {
important = this.important();
} else if (isVariable) {
// As a last resort, try permissiveValue
/**
* As a last resort, try permissiveValue
*
* @todo - This has created some knock-on problems of not
* flagging incorrect syntax or detecting user intent.
*/
value = this.permissiveValue();
}
}
@@ -1653,6 +1703,8 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
* First, it will try to parse comments and entities to reach
* the end. This is mostly like the Expression parser except no
* math is allowed.
*
* @param {RexExp} untilTokens - Characters to stop parsing at
*/
permissiveValue: function (untilTokens) {
let i;
@@ -1718,6 +1770,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
parserInput.forget();
return new tree.Anonymous('', index);
}
/** @type {string} */
let item;
for (i = 0; i < value.length; i++) {
item = value[i];
@@ -1731,10 +1784,16 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
}
// Treat like quoted values, but replace vars like unquoted expressions
const quote = new tree.Quoted('\'', item, true, index, fileInfo);
if (!item.startsWith('@{')) {
quote.variableRegex = /@([\w-]+)/g;
const variableRegex = /@([\w-]+)/g;
const propRegex = /\$([\w-]+)/g;
if (variableRegex.test(item)) {
warn('@[ident] in unknown values will not be evaluated as variables in the future. Use @{[ident]}', index, 'DEPRECATED');
}
quote.propRegex = /\$([\w-]+)/g;
if (propRegex.test(item)) {
warn('$[ident] in unknown values will not be evaluated as property references in the future. Use ${[ident]}', index, 'DEPRECATED');
}
quote.variableRegex = /@([\w-]+)|@{([\w-]+)}/g;
quote.propRegex = /\$([\w-]+)|\${([\w-]+)}/g;
result.push(quote);
}
}
@@ -2152,7 +2211,14 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
parserInput.save();
op = parserInput.$char('/') || parserInput.$char('*') || parserInput.$str('./');
op = parserInput.$char('/') || parserInput.$char('*');
if (!op) {
let index = parserInput.i;
op = parserInput.$str('./');
if (op) {
warn('./ operator is deprecated', index, 'DEPRECATED');
}
}
if (!op) { parserInput.forget(); break; }

View File

@@ -12,6 +12,7 @@ const Extend = function(selector, option, index, currentFileInfo, visibilityInfo
this.allowRoot = true;
switch (option) {
case '!all':
case 'all':
this.allowBefore = true;
this.allowAfter = true;

View File

@@ -33,12 +33,12 @@ Quoted.prototype = Object.assign(new Node(), {
eval(context) {
const that = this;
let value = this.value;
const variableReplacement = function (_, name) {
const v = new Variable(`@${name}`, that.getIndex(), that.fileInfo()).eval(context, true);
const variableReplacement = function (_, name1, name2) {
const v = new Variable(`@${name1 ?? name2}`, that.getIndex(), that.fileInfo()).eval(context, true);
return (v instanceof Quoted) ? v.value : v.toCSS();
};
const propertyReplacement = function (_, name) {
const v = new Property(`$${name}`, that.getIndex(), that.fileInfo()).eval(context, true);
const propertyReplacement = function (_, name1, name2) {
const v = new Property(`$${name1 ?? name2}`, that.getIndex(), that.fileInfo()).eval(context, true);
return (v instanceof Quoted) ? v.value : v.toCSS();
};
function iterativeReplace(value, regexp, replacementFnc) {

View File

@@ -133,7 +133,12 @@ class ProcessExtendsVisitor {
if (!indices[`${extend.index} ${selector}`]) {
indices[`${extend.index} ${selector}`] = true;
logger.warn(`extend '${selector}' has no matches`);
/**
* @todo Shouldn't this be an error? To alert the developer
* that they may have made an error in the selector they are
* targeting?
*/
logger.warn(`WARNING: extend '${selector}' has no matches`);
}
});
}

View File

@@ -12,7 +12,7 @@ logger.addListener({
warn(msg) {
process.stdout.write(msg + '\n');
},
erro(msg) {
error(msg) {
process.stdout.write(msg + '\n');
}
});
@@ -366,6 +366,8 @@ module.exports = function() {
var options = clone(originalOptions);
options.stylize = stylize;
var name = getBasename(file);
if (oneTestOnly && name !== oneTestOnly) {

View File

@@ -31,6 +31,11 @@ foo[attr="blah"] {
this: works;
}
}
@media (min-width: 640px) {
.with-curly {
this: works;
}
}
.test-rule-comment {
--value: a /* { ; } */;
--comment-within: ( /* okay?; comment; */ );

View File

@@ -59,7 +59,7 @@ div.ext5,
}
}
.fuu:extend(.ext8.ext9 all) {}
.fuu:extend( .ext8.ext9 all) {}
.buu:extend(.ext8 .ext9 all) {}
.zap:extend(.ext8 + .ext9 all) {}
.zoo:extend(.ext8 > .ext9 all) {}

View File

@@ -22,7 +22,7 @@
#container {
color: black;
.mixin();
.mixout();
.mixout ();
#theme > .mixin();
}

View File

@@ -43,6 +43,12 @@
this: works;
}
}
@tablet: (min-width: @{size});
@media @tablet {
.with-curly {
this: works;
}
}
// @todo - fix comment absorption after property
.test-rule-comment {
--value: a/* { ; } */;

View File

@@ -5,10 +5,7 @@
},
"version": "4.2.2",
"description": "Less files and CSS results",
"author": {
"name": "Alexis Sellier",
"email": "self@cloudhead.net"
},
"author": "Alexis Sellier <self@cloudhead.net>",
"contributors": [
"The Core Less Team"
],

View File

@@ -3,10 +3,7 @@
"private": true,
"version": "4.0.0",
"description": "Less files to be included in node_modules directory for testing import from node_modules",
"author": {
"name": "Alexis Sellier",
"email": "self@cloudhead.net"
},
"author": "Alexis Sellier <self@cloudhead.net>",
"contributors": [
"The Core Less Team"
],

9398
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

2
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,2 @@
packages:
- 'packages/*'