Skip to content

Commit d7b0ba3

Browse files
feat(import-assertions-to-attributes): introduce (#89)
1 parent 904297c commit d7b0ba3

File tree

17 files changed

+410
-104
lines changed

17 files changed

+410
-104
lines changed

biome.jsonc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
33
"files": {
4-
"includes": ["**", "!**/*.snap.cjs", "!**/fixtures/**"]
4+
"includes": ["**", "!**/*.snap.cjs", "!**/fixtures/**", "!**/expected/**", "!**/input/**"]
55
},
66
"assist": { "actions": { "source": { "organizeImports": "off" } } },
77
// Rules for the linter

package-lock.json

Lines changed: 109 additions & 102 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Import Assertions to Attributes
2+
3+
This recipe converts import assertions (`assert` syntax) to the standardized import attributes (`with` syntax). It modifies code like:
4+
5+
```ts
6+
// Before
7+
import data from './data.json' assert { type: 'json' };
8+
9+
// After
10+
import data from './data.json' with { type: 'json' };
11+
```
12+
13+
## Usage
14+
15+
Run this codemod with:
16+
17+
```sh
18+
npx codemod@next nodejs/import-assertions-to-attributes
19+
```
20+
21+
## When is it useful?
22+
23+
The import assertions syntax is being deprecated in favor of the standardized import attributes syntax. This codemod helps transition existing codebases to the new syntax, ensuring compatibility with future versions of Node.js.
24+
25+
Node.js drop support of import assertions in favor of import attributes in version [`22.0.0`](https://nodejs.org/fr/blog/release/v22.0.0#other-notable-changes)
26+
But the support for import attributes was added in Node.js version [`18.20.0`](https://nodejs.org/fr/blog/release/v18.20.0#added-support-for-import-attributes)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
schema_version: "1.0"
2+
name: nodejs/import-assertions-to-attributes
3+
version: 0.0.1
4+
description: Replace `assert` import attribute to the `with` ECMAScript import attribute.
5+
author: Augustin Mauroy
6+
license: MIT
7+
workflow: workflow.yaml
8+
category: migration
9+
10+
targets:
11+
languages:
12+
- javascript
13+
- typescript
14+
15+
keywords:
16+
- transformation
17+
- migration
18+
19+
registry:
20+
access: public
21+
visibility: public
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "@nodejs/import-assertions-to-attributes",
3+
"version": "0.0.1",
4+
"description": "Replace `assert` import attribute to the `with` ECMAScript import attribute.",
5+
"type": "module",
6+
"scripts": {
7+
"test": "npx codemod@next jssg test -l typescript ./src/workflow.ts ./"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/nodejs/userland-migrations.git",
12+
"directory": "recipes/import-assertions-to-attributes",
13+
"bugs": "https://github.com/nodejs/userland-migrations/issues"
14+
},
15+
"author": "Augustin Mauroy",
16+
"license": "MIT",
17+
"homepage": "https://github.com/nodejs/userland-migrations/tree/main/import-assertions-to-attributes#readme",
18+
"devDependencies": {
19+
"@types/node": "^24.0.3",
20+
"@codemod.com/jssg-types": "^1.0.3"
21+
}
22+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import type { SgRoot, Edit } from "@codemod.com/jssg-types/main";
2+
3+
/**
4+
* Transform function that converts import assertions to import attributes
5+
*
6+
* Handles:
7+
* 1. import { something } from './module.json' assert { type: 'json' };
8+
* 2. import('./module.json', { assert: { type: 'json' } })
9+
*
10+
* Converts them to:
11+
* 1. import { something } from './module.json' with { type: 'json' };
12+
* 2. import('./module.json', { with: { type: 'json' } })
13+
*/
14+
export default async function transform(root: SgRoot): Promise<string> {
15+
const rootNode = root.root();
16+
const edits: Edit[] = [];
17+
18+
const importStatements = rootNode.findAll({
19+
rule: {
20+
kind: 'import_attribute',
21+
regex: '^assert\\s*\\{',
22+
}
23+
});
24+
25+
for (const importNode of importStatements) {
26+
// Replace 'assert' with 'with' in the import statement
27+
importNode.children().map(child => {
28+
if (child.kind() === 'assert' && child.text() === 'assert') {
29+
//return child.replace('with');
30+
edits.push(child.replace('with'));
31+
}
32+
})
33+
}
34+
35+
// Handle dynamic import call expressions with assert attributes
36+
// e.g., import('./module.json', { assert: { type: 'json' } })
37+
const assertIdentifiers = rootNode.findAll({
38+
rule: {
39+
kind: 'property_identifier',
40+
regex: '^assert$',
41+
inside: {
42+
kind: 'pair',
43+
inside: {
44+
kind: 'object',
45+
inside: {
46+
kind: 'arguments',
47+
inside: {
48+
kind: 'call_expression',
49+
pattern: "import($_SPECIFIER, $_ARGS)"
50+
}
51+
}
52+
}
53+
}
54+
}
55+
});
56+
57+
for (const assertNode of assertIdentifiers) {
58+
edits.push(assertNode.replace('with'));
59+
}
60+
61+
return rootNode.commitEdits(edits);
62+
}
63+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { createRequire } from 'node:module';
2+
import data from './data.json' with { type: 'json' };
3+
import systemOfADown from './system;of;a;down.json' with { type: 'json' };
4+
import { default as config } from './config.json'with{type: 'json'};
5+
import { thing } from "./data.json"with{type: 'json'};
6+
import { fileURLToPath } from 'node:url' invalid { };
7+
const require = createRequire(import.meta.url);
8+
const foo = require('./foo.ts');
9+
10+
const data2 = await import('./data2.json', {
11+
with: { type: 'json' },
12+
});
13+
14+
await import('foo-bis');
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
async function main() {
2+
const data = await import('./data.json', { with: { type: 'json' } });
3+
const pkg = await import('pkg');
4+
5+
return data;
6+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import data from './data.json' with { type: 'json' };
2+
3+
const data2 = await import('./data2.json', {
4+
with: { type: 'json' },
5+
});
6+
7+
await import('./data3.json', {
8+
with: { type: 'json' },
9+
});
10+
11+
await import('pkg');
12+
13+
function getData4() {
14+
import('pkg-bis');
15+
16+
return import('./data4.json', {
17+
with: { type: 'json' },
18+
});
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import data from './data.json' with { type: 'json' };
2+
3+
const data2 = await import('./data2.json', {
4+
with: { type: 'json' },
5+
});
6+
7+
await import('./data3.json', {
8+
with: { type: 'json' },
9+
});
10+
11+
await import('pkg');
12+
13+
function getData4() {
14+
import('pkg-bis');
15+
16+
return import('./data4.json', {
17+
with: { type: 'json' },
18+
});
19+
}

0 commit comments

Comments
 (0)