Skip to content

Support ESM Modules #93

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
dist/
add_esm_import_extensions.mjs
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v16.17.1
11 changes: 11 additions & 0 deletions History.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
4.1.0 / 2023-01-25
==================

* Support ESM Modules

4.0.2 / 2023-01-12
==================

* #71 : @import does not work if url contains ';'
* #77 : Regression in selector parsing: Attribute selectors not parsed correctly

4.0.1 / 2022-08-03
==================

Expand Down
16 changes: 8 additions & 8 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ CSS parser / stringifier.

## Installation

$ npm install css
$ npm install @adobe/css-tools

## Usage

```js
var css = require('css');
var obj = css.parse('body { font-size: 12px; }', options);
css.stringify(obj, options);
import { parse, stringify } from '@adobe/css-tools'
let obj = parse('body { font-size: 12px; }', options);
let css = stringify(obj, options);
```

## API

### css.parse(code, [options])
### parse(code, [options])

Accepts a CSS string and returns an AST `object`.

Expand All @@ -28,7 +28,7 @@ Accepts a CSS string and returns an AST `object`.
- source: the path to the file containing `css`. Makes errors and source
maps more helpful, by letting them know where code comes from.

### css.stringify(object, [options])
### stringify(object, [options])

Accepts an AST `object` (as `css.parse` produces) and returns a CSS string.

Expand All @@ -40,9 +40,9 @@ Accepts an AST `object` (as `css.parse` produces) and returns a CSS string.
### Example

```js
var ast = css.parse('body { font-size: 12px; }', { source: 'source.css' });
var ast = parse('body { font-size: 12px; }', { source: 'source.css' });

var css = css.stringify(ast);
var css = stringify(ast);
```

### Errors
Expand Down
105 changes: 105 additions & 0 deletions add_esm_import_extensions.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import * as fs from 'fs';
import * as path from 'path';

// https://gist.github.com/lovasoa/8691344
async function* walk(dir) {
for await (const d of await fs.promises.opendir(dir)) {
const entry = path.join(dir, d.name);
if (d.isDirectory()) {
yield* walk(entry);
} else if (d.isFile()) {
yield entry;
}
}
}

function resolveImportPath(sourceFile, importPath, options) {
const sourceFileAbs = path.resolve(process.cwd(), sourceFile);
const root = path.dirname(sourceFileAbs);
const {moduleFilter = defaultModuleFilter} = options;

if (moduleFilter(importPath)) {
const importPathAbs = path.resolve(root, importPath);
const possiblePath = [
path.resolve(importPathAbs, './index.ts'),
path.resolve(importPathAbs, './index.js'),
importPathAbs + '.ts',
importPathAbs + '.js',
];

if (possiblePath.length) {
for (let i = 0; i < possiblePath.length; i++) {
const entry = possiblePath[i];
if (fs.existsSync(entry)) {
const resolved = path.relative(root, entry.replace(/\.ts$/, '.js'));

if (!resolved.startsWith('.')) {
return './' + resolved;
}

return resolved;
}
}
}
}

return null;
}

function replace(filePath, outFilePath, options) {
const code = fs.readFileSync(filePath).toString();
const newCode = code.replace(
/(import|export) (.+?) from ('[^\n']+'|"[^\n"]+");/gs,
(found, action, imported, from) => {
const importPath = from.slice(1, -1);
const resolvedPath = resolveImportPath(filePath, importPath, options);

if (resolvedPath) {
console.log('\t', importPath, resolvedPath);
return `${action} ${imported} from '${resolvedPath.replaceAll(
'\\',
'/'
)}';`;
}

return found;
}
);

if (code !== newCode) {
fs.writeFileSync(outFilePath, newCode);
}
}

// Then, use it with a simple async for loop
async function run(srcDir, options = defaultOptions) {
const {sourceFileFilter = defaultSourceFileFilter} = options;

for await (const entry of walk(srcDir)) {
if (sourceFileFilter(entry)) {
console.log(entry);
replace(entry, entry, options);
}
}
}

const defaultSourceFileFilter = function (sourceFilePath) {
return (
/\.(js|ts)$/.test(sourceFilePath) && !/node_modules/.test(sourceFilePath)
);
};

const defaultModuleFilter = function (importedModule) {
return (
!path.isAbsolute(importedModule) &&
!importedModule.startsWith('@') &&
!importedModule.endsWith('.js')
);
};

const defaultOptions = {
sourceFileFilter: defaultSourceFileFilter,
moduleFilter: defaultModuleFilter,
};

run('./dist/esm', defaultOptions);
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
{
"name": "@adobe/css-tools",
"version": "4.0.2",
"version": "4.1.0",
"description": "CSS parser / stringifier",
"main": "dist/umd/cssTools.js",
"module": "dist/cjs/cssTools.js",
"exports": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/cssTools.js"
},
"types": "dist/cjs/index.d.ts",
"files": [
"dist",
Expand All @@ -30,8 +34,9 @@
"test": "jest",
"lint": "gts lint",
"clean": "gts clean",
"build": "npm run compile",
"compile": "NODE_ENV=prod webpack --mode production",
"build": "npm run compile && npm run buildesm",
"buildesm": "tsc --project tsconfig.esm.json && node ./add_esm_import_extensions.mjs",
"compile": "set NODE_ENV=prod & webpack --mode production",
"fix": "gts fix",
"prepare": "npm run build",
"pretest": "npm run build",
Expand Down
12 changes: 12 additions & 0 deletions tsconfig.esm.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "node",
"rootDir": "src",
"outDir": "dist/esm",
},
"include": [
"src/**/*.ts"
]
}