Skip to content

Commit

Permalink
fix: πŸ› watch mode (#10)
Browse files Browse the repository at this point in the history
βœ… Closes: #9
  • Loading branch information
iam-medvedev authored Apr 1, 2021
1 parent 59c8463 commit 3480415
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 15 deletions.
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-f8bc45.svg)](https://github.com/prettier/prettier)
[![npm version](https://badge.fury.io/js/esbuild-plugin-less.svg)](https://www.npmjs.com/package/esbuild-plugin-less)
[![David](https://img.shields.io/david/dev/iam-medvedev/esbuild-plugin-less)](https://david-dm.org/iam-medvedev/esbuild-plugin-less)
[![David](https://status.david-dm.org/gh/iam-medvedev/esbuild-plugin-less.svg?type=dev)](https://david-dm.org/iam-medvedev/esbuild-plugin-less)
[![David](https://status.david-dm.org/gh/iam-medvedev/esbuild-plugin-less.svg?type=peer)](https://david-dm.org/iam-medvedev/esbuild-plugin-less)
[![Codecov](https://img.shields.io/codecov/c/github/iam-medvedev/esbuild-plugin-less)](https://codecov.io/gh/iam-medvedev/esbuild-plugin-less)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fiam-medvedev%2Fesbuild-plugin-less.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fiam-medvedev%2Fesbuild-plugin-less?ref=badge_shield)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
Expand All @@ -19,6 +20,8 @@ yarn add esbuild-plugin-less -D

## Usage

### Simple example

You can see the example [here](./example).

```ts
Expand All @@ -36,6 +39,26 @@ build({
});
```

### Watch mode

More information about watch mode [here](https://esbuild.github.io/api/#watch).

```ts
import { build } from 'esbuild';
import { lessLoader } from 'esbuild-plugin-less';

build({
watch: true, // enable watch mode
entryPoints: [path.resolve(__dirname, 'index.ts')],
bundle: true,
outdir: path.resolve(__dirname, 'output'),
plugins: [lessLoader()],
loader: {
'.ts': 'ts',
},
});
```

## Options

`lessLoader` accepts all valid options from less.js. You can find a complete list of options [here](http://lesscss.org/usage/#less-options).
Expand Down
18 changes: 18 additions & 0 deletions __tests__/less-utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import path from 'path';
import { getLessImports } from '../src/less-utils';

describe('less-utils', () => {
it('get imports paths', () => {
const filePath = path.resolve(__dirname, '../example/styles/style.less');
const imports = getLessImports(filePath);

expect(imports).toEqual(
expect.arrayContaining([
expect.stringContaining('styles/style-2.less'),
expect.stringContaining('styles/inner/style-3.less'),
expect.stringContaining('styles/inner/style-4.css'),
expect.stringContaining('styles/inner/style-5.css'),
]),
);
});
});
14 changes: 14 additions & 0 deletions example/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,21 @@ import * as path from 'path';
import { build } from 'esbuild';
import { lessLoader } from '../src/index';

// isProduction flag for watch mode
const isProduction = process.env.NODE_ENV === 'production';

build({
watch: isProduction
? false
: {
onRebuild(error) {
if (error) {
console.error('Build failed:', error);
} else {
console.error('Build succeeded');
}
},
},
entryPoints: [path.resolve(__dirname, 'index.ts')],
bundle: true,
outdir: path.resolve(__dirname, 'output'),
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
],
"scripts": {
"build": "NODE_ENV=production ts-node ./scripts/build.ts",
"build:example": "ts-node ./example/build.ts",
"dev:example": "ts-node ./example/build.ts",
"build:example": "NODE_ENV=production ts-node ./example/build.ts",
"commit": "yarn git-cz",
"prepublish": "yarn test && yarn types && yarn build",
"types": "NODE_ENV=production tsc --emitDeclarationOnly --declaration --outDir build",
Expand All @@ -40,7 +41,7 @@
"@types/jest": "^26.0.20",
"@types/node": "^14.14.22",
"cz-conventional-changelog": "^3.3.0",
"esbuild": "^0.8.54",
"esbuild": "^0.11.2",
"git-cz": "^4.7.6",
"husky": "^5.1.3",
"jest": "^26.6.3",
Expand All @@ -52,7 +53,7 @@
"typescript": "^4.1.3"
},
"peerDependencies": {
"esbuild": "^0.8.x"
"esbuild": "^0.11.x"
},
"dependencies": {
"@types/less": "^3.0.2",
Expand Down
15 changes: 8 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import * as path from 'path';
import path from 'path';
import { promises as fs } from 'fs';
import { Plugin } from 'esbuild';
import less from 'less';

const namespace = 'less';
import { getLessImports } from './less-utils';

/** Less-loader for esbuild */
export function lessLoader(options: Less.Options = {}): Plugin {
return {
name: 'less-loader',
setup: (build) => {
// Resolve *.less files with namespace
build.onResolve({ filter: /\.less$/ }, (args) => {
build.onResolve({ filter: /\.less$/, namespace: 'file' }, (args) => {
const filePath = path.resolve(process.cwd(), path.relative(process.cwd(), args.resolveDir), args.path);

return {
path: path.resolve(process.cwd(), path.relative(process.cwd(), args.resolveDir), args.path),
namespace,
path: filePath,
watchFiles: !!build.initialOptions.watch ? [filePath, ...getLessImports(filePath)] : undefined,
};
});

// Build .less files
build.onLoad({ filter: /.*/, namespace }, async (args) => {
build.onLoad({ filter: /\.less$/, namespace: 'file' }, async (args) => {
const content = await fs.readFile(args.path, 'utf-8');
const dir = path.dirname(args.path);
const filename = path.basename(args.path);
Expand Down
37 changes: 37 additions & 0 deletions src/less-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import fs from 'fs';
import path from 'path';

const importRegex = /@import(?:\s+\((.*)\))?\s+['"](.*)['"]/;
const globalImportRegex = /@import(?:\s+\((.*)\))?\s+['"](.*)['"]/g;
const importCommentRegex = /(?:\/\*(?:[\s\S]*?)\*\/)|(\/\/(?:.*)$)/gm;

const extWhitelist = ['.css', '.less'];

/** Recursively get .less/.css imports from file */
export const getLessImports = (filePath: string): string[] => {
try {
const dir = path.dirname(filePath);
const content = fs.readFileSync(filePath).toString('utf8');

const cleanContent = content.replace(importCommentRegex, '');
const match = cleanContent.match(globalImportRegex) || [];

const fileImports = match
.map((el) => {
const match = el.match(importRegex);
return match[2];
})
.filter((el) => !!el)
.map((el) => path.resolve(dir, el));

const recursiveImports = fileImports.reduce((result, el) => {
return [...result, ...getLessImports(el)];
}, fileImports);

const result = recursiveImports.filter((el) => extWhitelist.includes(path.extname(el).toLowerCase()));

return result;
} catch (e) {
return [];
}
};
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2359,10 +2359,10 @@ es6-promisify@^5.0.0:
dependencies:
es6-promise "^4.0.3"

esbuild@^0.8.54:
version "0.8.54"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.8.54.tgz#2f32ff80e95c69a0f25b799d76a27c05e2857cdf"
integrity sha512-DJH38OiTgXJxFb/EhHrCrY8eGmtdkTtWymHpN9IYN9AF+4jykT0dQArr7wzFejpVbaB0TMIq2+vfNRWr3LXpvw==
esbuild@^0.11.2:
version "0.11.2"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.11.2.tgz#3b995e107f2054d9090402b98a3b79ceffd05eb6"
integrity sha512-8d5FCQrR+juXC2u9zjTQ3+IYiuFuaWyKYwmApFJLquTrYNbk36H/+MkRQeTuOJg7IjUchRX2Ulwo1zRYXZ1pUg==

escalade@^3.1.1:
version "3.1.1"
Expand Down

0 comments on commit 3480415

Please sign in to comment.