diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml
index 99f194da6..0db35d2f1 100644
--- a/.github/ISSUE_TEMPLATE/bug-report.yml
+++ b/.github/ISSUE_TEMPLATE/bug-report.yml
@@ -9,6 +9,8 @@ body:
attributes:
label: Which packages are affected?
options:
+ # NOTE: Package names are automatically generated. Do not manually edit.
+ # packages-start
- label: "`@eslint/compat`"
required: false
- label: "`@eslint/config-array`"
@@ -21,6 +23,8 @@ body:
required: false
- label: "`@eslint/plugin-kit`"
required: false
+ # packages-end
+
- type: textarea
attributes:
label: Environment
diff --git a/.github/ISSUE_TEMPLATE/change.yml b/.github/ISSUE_TEMPLATE/change.yml
index a77de3ea9..551c780d3 100644
--- a/.github/ISSUE_TEMPLATE/change.yml
+++ b/.github/ISSUE_TEMPLATE/change.yml
@@ -8,6 +8,8 @@ body:
attributes:
label: Which packages would you like to change?
options:
+ # NOTE: Package names are automatically generated. Do not manually edit.
+ # packages-start
- label: "`@eslint/compat`"
required: false
- label: "`@eslint/config-array`"
@@ -20,6 +22,8 @@ body:
required: false
- label: "`@eslint/plugin-kit`"
required: false
+ # packages-end
+
- type: textarea
attributes:
label: What problem do you want to solve?
diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml
new file mode 100644
index 000000000..dd525c798
--- /dev/null
+++ b/.github/workflows/manual-publish.yml
@@ -0,0 +1,81 @@
+name: Manual Package Publish
+
+on:
+ workflow_dispatch:
+ inputs:
+ package:
+ description: "Package to publish"
+ required: true
+ type: choice
+ options:
+ # NOTE: Package names are automatically generated. Do not manually edit.
+ # packages-start
+ - compat
+ - config-array
+ - core
+ - migrate-config
+ - object-schema
+ - plugin-kit
+ # packages-end
+
+permissions:
+ contents: read
+ id-token: write
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: lts/*
+ registry-url: "https://registry.npmjs.org"
+
+ - name: Install dependencies
+ run: npm install
+
+ - name: Get latest release tag
+ id: get-latest-release
+ run: |
+ LATEST_TAG=$(git tag -l "${{ inputs.package }}*" --sort=-v:refname | head -n 1)
+ echo "latest_tag=${LATEST_TAG}" >> $GITHUB_OUTPUT
+
+ - name: Check out latest release
+ id: checkout-latest-release
+ run: |
+ git fetch --tags origin ${{ steps.get-latest-release.outputs.latest_tag }}
+ git checkout ${{ steps.get-latest-release.outputs.latest_tag }}
+
+ - name: Get package version
+ id: get-version
+ run: |
+ VERSION=$(node -p "require('./packages/${{ inputs.package }}/package.json').version")
+ echo "version=${VERSION}" >> $GITHUB_OUTPUT
+
+ - name: Build
+ run: npm run build
+
+ - name: Publish to npm
+ run: npm publish -w packages/${{ inputs.package }} --provenance
+ env:
+ NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
+
+ - name: Publish to JSR
+ if: ${{ inputs.package != 'migrate-config' }}
+ run: npx jsr publish
+ working-directory: packages/${{ inputs.package }}
+
+ - name: Post Release Announcement
+ run: npx @humanwhocodes/crosspost -t -b -m "eslint/${{ inputs.package }} v${{ steps.get-version.outputs.version }} has been released!\n\nhttps://github.com/eslint/rewrite/releases/tag/${{ steps.get-latest-release.outputs.latest_tag }}"
+ env:
+ TWITTER_API_CONSUMER_KEY: ${{ secrets.TWITTER_CONSUMER_KEY }}
+ TWITTER_API_CONSUMER_SECRET: ${{ secrets.TWITTER_CONSUMER_SECRET }}
+ TWITTER_ACCESS_TOKEN_KEY: ${{ secrets.TWITTER_ACCESS_TOKEN_KEY }}
+ TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
+ MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }}
+ MASTODON_HOST: ${{ secrets.MASTODON_HOST }}
+ BLUESKY_IDENTIFIER: ${{ vars.BLUESKY_IDENTIFIER }}
+ BLUESKY_PASSWORD: ${{ secrets.BLUESKY_PASSWORD }}
+ BLUESKY_HOST: ${{ vars.BLUESKY_HOST }}
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 883f49126..a4eafcdec 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,8 +1,8 @@
{
- "packages/compat": "1.2.6",
+ "packages/compat": "1.2.7",
"packages/config-array": "0.19.2",
- "packages/core": "0.11.0",
- "packages/migrate-config": "1.3.7",
+ "packages/core": "0.12.0",
+ "packages/migrate-config": "1.3.8",
"packages/object-schema": "2.1.6",
- "packages/plugin-kit": "0.2.6"
+ "packages/plugin-kit": "0.2.7"
}
diff --git a/README.md b/README.md
index 22fa428f2..6fb719c80 100644
--- a/README.md
+++ b/README.md
@@ -6,11 +6,18 @@ Monorepo for the rewrite of ESLint.
This repository is the home of the following packages:
+
+
+
+
- [`@eslint/core`](packages/core)
- [`@eslint/compat`](packages/compat)
- [`@eslint/config-array`](packages/config-array)
- [`@eslint/object-schema`](packages/object-schema)
- [`@eslint/migrate-config`](packages/migrate-config)
+
+
+
@@ -23,8 +30,8 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
Platinum Sponsors

Gold Sponsors

Silver Sponsors
-

Bronze Sponsors
-

+

Bronze Sponsors
+

Technology Sponsors
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.

diff --git a/package.json b/package.json
index 687926a53..1914f7dfa 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
"test": "npm test --workspaces --if-present",
"build": "node scripts/build.js",
"build:readme": "node tools/update-readme.js",
+ "build:new-pkg": "node tools/new-pkg.js",
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"fmt": "prettier --write .",
@@ -27,6 +28,9 @@
],
"!(*.{js,ts})": "prettier --write --ignore-unknown"
},
+ "engines": {
+ "node": ">= 22.3.0"
+ },
"devDependencies": {
"@types/mocha": "^10.0.7",
"eslint": "^9.11.1",
diff --git a/packages/compat/CHANGELOG.md b/packages/compat/CHANGELOG.md
index ec64b70d6..16319c2c6 100644
--- a/packages/compat/CHANGELOG.md
+++ b/packages/compat/CHANGELOG.md
@@ -1,5 +1,14 @@
# Changelog
+## [1.2.7](https://github.com/eslint/rewrite/compare/compat-v1.2.6...compat-v1.2.7) (2025-02-21)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * devDependencies
+ * @eslint/core bumped from ^0.11.0 to ^0.12.0
+
## [1.2.6](https://github.com/eslint/rewrite/compare/compat-v1.2.5...compat-v1.2.6) (2025-01-31)
diff --git a/packages/compat/README.md b/packages/compat/README.md
index a0a860b22..c707b0098 100644
--- a/packages/compat/README.md
+++ b/packages/compat/README.md
@@ -198,8 +198,8 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
Platinum Sponsors

Gold Sponsors

Silver Sponsors
-

Bronze Sponsors
-

+

Bronze Sponsors
+

Technology Sponsors
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.

diff --git a/packages/compat/jsr.json b/packages/compat/jsr.json
index 9cf6ad2d5..fe46febbc 100644
--- a/packages/compat/jsr.json
+++ b/packages/compat/jsr.json
@@ -1,6 +1,6 @@
{
"name": "@eslint/compat",
- "version": "1.2.6",
+ "version": "1.2.7",
"exports": "./dist/esm/index.js",
"publish": {
"include": [
diff --git a/packages/compat/package.json b/packages/compat/package.json
index 88761cef2..c0efeff54 100644
--- a/packages/compat/package.json
+++ b/packages/compat/package.json
@@ -1,6 +1,6 @@
{
"name": "@eslint/compat",
- "version": "1.2.6",
+ "version": "1.2.7",
"description": "Compatibility utilities for ESLint",
"type": "module",
"main": "dist/esm/index.js",
@@ -48,7 +48,7 @@
},
"homepage": "https://github.com/eslint/rewrite#readme",
"devDependencies": {
- "@eslint/core": "^0.11.0",
+ "@eslint/core": "^0.12.0",
"c8": "^9.1.0",
"eslint": "^9.11.0",
"mocha": "^10.4.0",
diff --git a/packages/config-array/README.md b/packages/config-array/README.md
index 4a2511e8e..d6df54e57 100644
--- a/packages/config-array/README.md
+++ b/packages/config-array/README.md
@@ -350,8 +350,8 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
Platinum Sponsors

Gold Sponsors

Silver Sponsors
-

Bronze Sponsors
-

+

Bronze Sponsors
+

Technology Sponsors
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.

diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md
index f0c1c0fdf..001ea71ff 100644
--- a/packages/core/CHANGELOG.md
+++ b/packages/core/CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog
+## [0.12.0](https://github.com/eslint/rewrite/compare/core-v0.11.0...core-v0.12.0) (2025-02-21)
+
+
+### Features
+
+* Add meta.language and meta.dialects to RuleDefinition ([#156](https://github.com/eslint/rewrite/issues/156)) ([dac0387](https://github.com/eslint/rewrite/commit/dac0387fc3dd7e74811ae045ab782c70366bb14c))
+
+
+### Bug Fixes
+
+* Types to align with older ESLint types ([#155](https://github.com/eslint/rewrite/issues/155)) ([664740a](https://github.com/eslint/rewrite/commit/664740a8d4a93bee896cec3a661bf2072e893e24))
+
## [0.11.0](https://github.com/eslint/rewrite/compare/core-v0.10.0...core-v0.11.0) (2025-01-31)
diff --git a/packages/core/README.md b/packages/core/README.md
index cbedf3e8a..1a93f30fa 100644
--- a/packages/core/README.md
+++ b/packages/core/README.md
@@ -21,8 +21,8 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
Platinum Sponsors

Gold Sponsors

Silver Sponsors
-

Bronze Sponsors
-

+

Bronze Sponsors
+

Technology Sponsors
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.

diff --git a/packages/core/jsr.json b/packages/core/jsr.json
index 30c6cb902..ea592b886 100644
--- a/packages/core/jsr.json
+++ b/packages/core/jsr.json
@@ -1,6 +1,6 @@
{
"name": "@eslint/core",
- "version": "0.11.0",
+ "version": "0.12.0",
"exports": "./dist/esm/types.d.ts",
"publish": {
"include": [
diff --git a/packages/core/package.json b/packages/core/package.json
index 6e683aa6c..7b7648b5b 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
{
"name": "@eslint/core",
- "version": "0.11.0",
+ "version": "0.12.0",
"description": "Runtime-agnostic core of ESLint",
"type": "module",
"types": "./dist/esm/types.d.ts",
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 1966377cd..a8f23509f 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -186,6 +186,16 @@ export interface RulesMeta<
* Indicates if the rule may provide suggestions.
*/
hasSuggestions?: boolean | undefined;
+
+ /**
+ * The language the rule is intended to lint.
+ */
+ language?: string;
+
+ /**
+ * The dialects of `language` that the rule is intended to lint.
+ */
+ dialects?: string[];
}
/**
@@ -459,6 +469,15 @@ export interface RuleTextEdit {
// #endregion
+/**
+ * Fixes a violation.
+ * @param fixer The text editor to apply the fix.
+ * @returns The fix(es) for the violation.
+ */
+type RuleFixer = (
+ fixer: RuleTextEditor,
+) => RuleTextEdit | Iterable | null;
+
interface ViolationReportBase {
/**
* The type of node that the violation is for.
@@ -473,22 +492,22 @@ interface ViolationReportBase {
/**
* The fix to be applied for the violation.
- * @param fixer The text editor to apply the fix.
- * @returns The fix(es) for the violation.
*/
- fix?(fixer: RuleTextEditor): RuleTextEdit | Iterable | null;
+ fix?: RuleFixer | null | undefined;
/**
* An array of suggested fixes for the problem. These fixes may change the
* behavior of the code, so they are not applied automatically.
*/
- suggest?: SuggestedEdit[];
+ suggest?: SuggestedEdit[] | null | undefined;
}
type ViolationMessage =
| { message: string }
| { messageId: MessageIds };
-type ViolationLocation = { loc: SourceLocation } | { node: Node };
+type ViolationLocation =
+ | { loc: SourceLocation | Position }
+ | { node: Node };
export type ViolationReport<
Node = unknown,
@@ -507,10 +526,8 @@ interface SuggestedEditBase {
/**
* The fix to be applied for the suggestion.
- * @param fixer The text editor to apply the fix.
- * @returns The fix for the suggestion.
*/
- fix?(fixer: RuleTextEditor): RuleTextEdit | Iterable | null;
+ fix?: RuleFixer | null | undefined;
}
type SuggestionMessage = { desc: string } | { messageId: string };
diff --git a/packages/core/tests/types/types.test.ts b/packages/core/tests/types/types.test.ts
index 81af92ec3..fcb70c13a 100644
--- a/packages/core/tests/types/types.test.ts
+++ b/packages/core/tests/types/types.test.ts
@@ -236,6 +236,8 @@ const testRule: RuleDefinition<{
badFoo: "change this foo",
wrongBar: "fix this bar",
},
+ language: "javascript",
+ dialects: ["javascript", "typescript"],
},
create(context: TestRuleContext): TestRuleVisitor {
@@ -257,6 +259,7 @@ const testRule: RuleDefinition<{
: "👎",
);
},
+ suggest: undefined,
});
}
},
@@ -268,10 +271,20 @@ const testRule: RuleDefinition<{
suggest: [
{
messageId: "Bar",
+ fix: null,
},
],
});
},
+ Baz(node: TestNode) {
+ // node.type === "Baz"
+ context.report({
+ message: "This baz is foobar",
+ loc: { line: node.start, column: 1 },
+ fix: null,
+ suggest: null,
+ });
+ },
};
},
};
diff --git a/packages/migrate-config/CHANGELOG.md b/packages/migrate-config/CHANGELOG.md
index 81954dc64..54a5de9a5 100644
--- a/packages/migrate-config/CHANGELOG.md
+++ b/packages/migrate-config/CHANGELOG.md
@@ -1,5 +1,14 @@
# Changelog
+## [1.3.8](https://github.com/eslint/rewrite/compare/migrate-config-v1.3.7...migrate-config-v1.3.8) (2025-02-21)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @eslint/compat bumped from ^1.2.6 to ^1.2.7
+
## [1.3.7](https://github.com/eslint/rewrite/compare/migrate-config-v1.3.6...migrate-config-v1.3.7) (2025-01-31)
diff --git a/packages/migrate-config/README.md b/packages/migrate-config/README.md
index 26fd7e0ed..be1718e45 100644
--- a/packages/migrate-config/README.md
+++ b/packages/migrate-config/README.md
@@ -102,8 +102,8 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
Platinum Sponsors

Gold Sponsors

Silver Sponsors
-

Bronze Sponsors
-

+

Bronze Sponsors
+

Technology Sponsors
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.

diff --git a/packages/migrate-config/package.json b/packages/migrate-config/package.json
index b4caee874..2a5389586 100644
--- a/packages/migrate-config/package.json
+++ b/packages/migrate-config/package.json
@@ -1,6 +1,6 @@
{
"name": "@eslint/migrate-config",
- "version": "1.3.7",
+ "version": "1.3.8",
"description": "Configuration migration for ESLint",
"type": "module",
"bin": {
@@ -46,7 +46,7 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"dependencies": {
- "@eslint/compat": "^1.2.6",
+ "@eslint/compat": "^1.2.7",
"@eslint/eslintrc": "^3.1.0",
"camelcase": "^8.0.0",
"recast": "^0.23.7"
diff --git a/packages/object-schema/README.md b/packages/object-schema/README.md
index 57191afbb..5e522357c 100644
--- a/packages/object-schema/README.md
+++ b/packages/object-schema/README.md
@@ -234,8 +234,8 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
Platinum Sponsors

Gold Sponsors

Silver Sponsors
-

Bronze Sponsors
-

+

Bronze Sponsors
+

Technology Sponsors
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.

diff --git a/packages/plugin-kit/CHANGELOG.md b/packages/plugin-kit/CHANGELOG.md
index 1dc9a3d00..6386e734f 100644
--- a/packages/plugin-kit/CHANGELOG.md
+++ b/packages/plugin-kit/CHANGELOG.md
@@ -1,5 +1,14 @@
# Changelog
+## [0.2.7](https://github.com/eslint/rewrite/compare/plugin-kit-v0.2.6...plugin-kit-v0.2.7) (2025-02-21)
+
+
+### Dependencies
+
+* The following workspace dependencies were updated
+ * dependencies
+ * @eslint/core bumped from ^0.11.0 to ^0.12.0
+
## [0.2.6](https://github.com/eslint/rewrite/compare/plugin-kit-v0.2.5...plugin-kit-v0.2.6) (2025-01-31)
diff --git a/packages/plugin-kit/README.md b/packages/plugin-kit/README.md
index f99d15acc..074844f79 100644
--- a/packages/plugin-kit/README.md
+++ b/packages/plugin-kit/README.md
@@ -265,8 +265,8 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
Platinum Sponsors

Gold Sponsors

Silver Sponsors
-

Bronze Sponsors
-

+

Bronze Sponsors
+

Technology Sponsors
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.

diff --git a/packages/plugin-kit/jsr.json b/packages/plugin-kit/jsr.json
index 7387415f3..f4291a882 100644
--- a/packages/plugin-kit/jsr.json
+++ b/packages/plugin-kit/jsr.json
@@ -1,6 +1,6 @@
{
"name": "@eslint/plugin-kit",
- "version": "0.2.6",
+ "version": "0.2.7",
"exports": "./dist/esm/index.js",
"publish": {
"include": [
diff --git a/packages/plugin-kit/package.json b/packages/plugin-kit/package.json
index 65b9ee979..99e96d260 100644
--- a/packages/plugin-kit/package.json
+++ b/packages/plugin-kit/package.json
@@ -1,6 +1,6 @@
{
"name": "@eslint/plugin-kit",
- "version": "0.2.6",
+ "version": "0.2.7",
"description": "Utilities for building ESLint plugins.",
"author": "Nicholas C. Zakas",
"type": "module",
@@ -47,7 +47,7 @@
],
"license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.11.0",
+ "@eslint/core": "^0.12.0",
"levn": "^0.4.1"
},
"devDependencies": {
diff --git a/templates/package/CHANGELOG.md b/templates/package/CHANGELOG.md
new file mode 100644
index 000000000..825c32f0d
--- /dev/null
+++ b/templates/package/CHANGELOG.md
@@ -0,0 +1 @@
+# Changelog
diff --git a/templates/package/LICENSE b/templates/package/LICENSE
new file mode 100644
index 000000000..261eeb9e9
--- /dev/null
+++ b/templates/package/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/templates/package/README.md b/templates/package/README.md
new file mode 100644
index 000000000..b625319ea
--- /dev/null
+++ b/templates/package/README.md
@@ -0,0 +1,37 @@
+# @eslint/<%= name %>
+
+## Description
+
+<%= description %>
+
+## Installation
+
+For Node.js and compatible runtimes:
+
+```shell
+npm install @eslint/<%= name %>
+# or
+yarn add @eslint/<%= name %>
+# or
+pnpm install @eslint/<%= name %>
+# or
+bun install @eslint/<%= name %>
+```
+
+For Deno:
+
+```shell
+deno add @eslint/<%= name %>
+```
+
+## Usage
+
+TODO
+
+## License
+
+Apache 2.0
+
+
+
+
diff --git a/templates/package/jsr.json b/templates/package/jsr.json
new file mode 100644
index 000000000..3c9fa1540
--- /dev/null
+++ b/templates/package/jsr.json
@@ -0,0 +1,16 @@
+{
+ "name": "@eslint/<%= name %>",
+ "version": "0.0.0",
+ "exports": "./dist/esm/index.js",
+ "publish": {
+ "include": [
+ "dist/esm/index.js",
+ "dist/esm/index.d.ts",
+ "dist/esm/types.ts",
+ "dist/esm/types.d.ts",
+ "README.md",
+ "jsr.json",
+ "LICENSE"
+ ]
+ }
+}
diff --git a/templates/package/package.json b/templates/package/package.json
new file mode 100644
index 000000000..5d270de0a
--- /dev/null
+++ b/templates/package/package.json
@@ -0,0 +1,58 @@
+{
+ "name": "@eslint/<%= name %>",
+ "version": "0.0.0",
+ "description": "<%= description %>",
+ "type": "module",
+ "main": "dist/esm/index.js",
+ "types": "dist/esm/index.d.ts",
+ "exports": {
+ "require": {
+ "types": "./dist/cjs/index.d.cts",
+ "default": "./dist/cjs/index.cjs"
+ },
+ "import": {
+ "types": "./dist/esm/index.d.ts",
+ "default": "./dist/esm/index.js"
+ }
+ },
+ "files": [
+ "dist"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "directories": {
+ "test": "tests"
+ },
+ "scripts": {
+ "build:cts": "node ../../tools/build-cts.js dist/esm/index.d.ts dist/cjs/index.d.cts",
+ "build": "rollup -c && tsc -p tsconfig.esm.json && npm run build:cts",
+ "test:jsr": "npx jsr@latest publish --dry-run",
+ "test": "mocha tests/*.js",
+ "test:coverage": "c8 npm test"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/eslint/rewrite.git"
+ },
+ "keywords": [
+ "eslint"
+ ],
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/eslint/rewrite/issues"
+ },
+ "homepage": "https://github.com/eslint/rewrite/tree/main/packages/<%= name %>#readme",
+ "devDependencies": {
+ "@eslint/core": "^0.10.0",
+ "c8": "^9.1.0",
+ "eslint": "^9.11.0",
+ "mocha": "^10.4.0",
+ "rollup": "^4.16.2",
+ "rollup-plugin-copy": "^3.5.0",
+ "typescript": "^5.4.5"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+}
diff --git a/templates/package/rollup.config.js b/templates/package/rollup.config.js
new file mode 100644
index 000000000..5b813a884
--- /dev/null
+++ b/templates/package/rollup.config.js
@@ -0,0 +1,24 @@
+import copy from "rollup-plugin-copy";
+
+export default {
+ input: "src/index.js",
+ output: [
+ {
+ file: "dist/cjs/index.cjs",
+ format: "cjs",
+ },
+ {
+ file: "dist/esm/index.js",
+ format: "esm",
+ banner: '// @ts-self-types="./index.d.ts"',
+ },
+ ],
+ plugins: [
+ copy({
+ targets: [
+ { src: "src/types.ts", dest: "dist/cjs", rename: "types.cts" },
+ { src: "src/types.ts", dest: "dist/esm" },
+ ],
+ }),
+ ],
+};
diff --git a/templates/package/src/index.js b/templates/package/src/index.js
new file mode 100644
index 000000000..0010db6c8
--- /dev/null
+++ b/templates/package/src/index.js
@@ -0,0 +1,3 @@
+/**
+ * @fileoverview Main entrypoint for the package.
+ */
diff --git a/templates/package/src/types.ts b/templates/package/src/types.ts
new file mode 100644
index 000000000..9900e5f09
--- /dev/null
+++ b/templates/package/src/types.ts
@@ -0,0 +1,3 @@
+/**
+ * @fileoverview Types for this package.
+ */
diff --git a/templates/package/tests/index.test.js b/templates/package/tests/index.test.js
new file mode 100644
index 000000000..bb5a64364
--- /dev/null
+++ b/templates/package/tests/index.test.js
@@ -0,0 +1,3 @@
+/**
+ * @fileoverview Tests for package entrypoint
+ */
diff --git a/templates/package/tsconfig.esm.json b/templates/package/tsconfig.esm.json
new file mode 100644
index 000000000..7ce10923a
--- /dev/null
+++ b/templates/package/tsconfig.esm.json
@@ -0,0 +1,7 @@
+{
+ "extends": "./tsconfig.json",
+ "files": ["dist/esm/index.js"],
+ "compilerOptions": {
+ "strict": false
+ }
+}
diff --git a/templates/package/tsconfig.json b/templates/package/tsconfig.json
new file mode 100644
index 000000000..779639cfe
--- /dev/null
+++ b/templates/package/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "files": ["src/index.js"],
+ "compilerOptions": {
+ "outDir": "dist/esm",
+ "strict": true
+ }
+}
diff --git a/tools/new-pkg.js b/tools/new-pkg.js
new file mode 100644
index 000000000..ada28ff20
--- /dev/null
+++ b/tools/new-pkg.js
@@ -0,0 +1,254 @@
+/**
+ * @fileoverview Script to bootstrap a new package in the monorepo.
+ *
+ * node tools/new-pkg.js --name --desc
+ *
+ * @author Nicholas C. Zakas
+ */
+
+//-----------------------------------------------------------------------------
+// Imports
+//-----------------------------------------------------------------------------
+
+import { readFileSync, readdirSync, writeFileSync, cpSync } from "node:fs";
+import { parseArgs } from "node:util";
+
+//-----------------------------------------------------------------------------
+// Helpers
+//-----------------------------------------------------------------------------
+
+/**
+ * Recursively gets all files in a directory.
+ * @param {string} dir The directory to search.
+ * @param {string[]} fileList The list of files found so far.
+ */
+function getAllFiles(dir) {
+ const fileList = [];
+ const files = readdirSync(dir, { withFileTypes: true });
+
+ files.forEach(file => {
+ const filePath = `${dir}/${file.name}`;
+ if (file.isDirectory()) {
+ fileList.push(...getAllFiles(filePath));
+ } else {
+ fileList.push(filePath);
+ }
+ });
+
+ return fileList;
+}
+
+//-----------------------------------------------------------------------------
+// Data
+//-----------------------------------------------------------------------------
+
+const packageNames = readdirSync("./packages");
+
+//-----------------------------------------------------------------------------
+// Parse CLI
+//-----------------------------------------------------------------------------
+
+const options = {
+ name: {
+ type: "string",
+ },
+ desc: {
+ type: "string",
+ },
+};
+
+const { values } = parseArgs({ args: process.argv.slice(2), options });
+
+if (!values.name) {
+ throw new Error("--name is required.");
+}
+
+if (packageNames.includes(values.name)) {
+ throw new Error(`Package ${values.name} already exists.`);
+}
+
+if (!values.desc) {
+ throw new Error("--desc is required.");
+}
+
+packageNames.push(values.name);
+packageNames.sort();
+
+//-----------------------------------------------------------------------------
+// Creating the new directory
+//-----------------------------------------------------------------------------
+
+console.log("Creating new directory...");
+
+const templateDirectory = "./templates/package";
+const newDirectory = `./packages/${values.name}`;
+
+cpSync(templateDirectory, newDirectory, { recursive: true });
+
+const allFiles = getAllFiles(newDirectory);
+
+allFiles.forEach(filePath => {
+ const content = readFileSync(filePath, "utf8");
+ const newContent = content
+ .replace(/<%=\s*name\s*%>/gu, values.name)
+ .replace(/<%=\s*description\s*%>/gu, values.desc);
+
+ writeFileSync(filePath, newContent, "utf8");
+});
+
+console.log("✅ Created", newDirectory);
+
+//-----------------------------------------------------------------------------
+// Update issue templates
+//-----------------------------------------------------------------------------
+
+console.log("\nUpdating issue templates...");
+
+const issueTemplateFiles = readdirSync("./.github/ISSUE_TEMPLATE");
+
+issueTemplateFiles.forEach(file => {
+ const filePath = `./.github/ISSUE_TEMPLATE/${file}`;
+ const content = readFileSync(filePath, "utf8");
+
+ if (!content.includes("# packages-start")) {
+ return;
+ }
+
+ const lines = content.split(/\r?\n/gu);
+
+ const startIndex = lines.findIndex(line =>
+ line.includes("# packages-start"),
+ );
+ const endIndex = lines.findIndex(line => line.includes("# packages-end"));
+ const newLines = packageNames.map(
+ packageName =>
+ `${" ".repeat(14)}- label: "\`@eslint/${packageName}\`"\n${" ".repeat(16)}required: false`,
+ );
+
+ lines.splice(startIndex + 1, endIndex - startIndex - 1, ...newLines);
+
+ writeFileSync(filePath, lines.join("\n"), "utf8");
+
+ console.log("✅ Updated", filePath);
+});
+
+//-----------------------------------------------------------------------------
+// Update manual-publish.yml
+//-----------------------------------------------------------------------------
+
+console.log("\nUpdating manual-publish.yml...");
+
+const publishPath = "./.github/workflows/manual-publish.yml";
+const publishContent = readFileSync(publishPath, "utf8");
+const publishLines = publishContent.split(/\r?\n/gu);
+const publishStartIndex = publishLines.findIndex(line =>
+ line.includes("# packages-start"),
+);
+const publishEndIndex = publishLines.findIndex(line =>
+ line.includes("# packages-end"),
+);
+const newPublishLines = packageNames.map(
+ packageName => `${" ".repeat(20)}- ${packageName}`,
+);
+
+publishLines.splice(
+ publishStartIndex + 1,
+ publishEndIndex - publishStartIndex - 1,
+ ...newPublishLines,
+);
+
+writeFileSync(publishPath, publishLines.join("\n"), "utf8");
+
+console.log("✅ Updated", publishPath);
+
+//-----------------------------------------------------------------------------
+// Update README
+//-----------------------------------------------------------------------------
+
+console.log("\nUpdating README...");
+
+const readmePath = "./README.md";
+const readmeContent = readFileSync(readmePath, "utf8");
+const readmeLines = readmeContent.split(/\r?\n/gu);
+const readmeStartIndex = readmeLines.findIndex(line =>
+ line.includes(""),
+);
+const readmeEndIndex = readmeLines.findIndex(line =>
+ line.includes(""),
+);
+const newReadmeLines = packageNames.map(
+ packageName => `- [\`@eslint/${packageName}\`](./packages/${packageName})`,
+);
+
+readmeLines.splice(
+ readmeStartIndex + 1,
+ readmeEndIndex - readmeStartIndex - 1,
+ ...newReadmeLines,
+);
+
+writeFileSync(readmePath, readmeLines.join("\n"), "utf8");
+
+console.log("✅ Updated", readmePath);
+
+//-----------------------------------------------------------------------------
+// Update release-please-manifest.json
+//-----------------------------------------------------------------------------
+
+console.log("\nUpdating release-please-manifest.json...");
+
+const manifestPath = "./.release-please-manifest.json";
+const manifestContent = readFileSync(manifestPath, "utf8");
+let manifest = JSON.parse(manifestContent);
+
+manifest[`packages/${values.name}`] = "0.0.0";
+
+// sort manifest keys
+manifest = Object.fromEntries(
+ Object.entries(manifest).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)),
+);
+
+writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
+
+console.log("✅ Updated", manifestPath);
+
+//-----------------------------------------------------------------------------
+// Update release-please-config.json
+//-----------------------------------------------------------------------------
+
+console.log("\nUpdating release-please-config.json...");
+
+const configPath = "./release-please-config.json";
+const configContent = readFileSync(configPath, "utf8");
+const config = JSON.parse(configContent);
+
+config.packages[`packages/${values.name}`] = {
+ "release-type": "node",
+ "extra-files": [
+ {
+ type: "json",
+ path: "jsr.json",
+ jsonpath: "$.version",
+ },
+ ],
+};
+
+// sort the config.packages keys
+config.packages = Object.fromEntries(
+ Object.entries(config.packages).sort(([keyA], [keyB]) =>
+ keyA.localeCompare(keyB),
+ ),
+);
+
+writeFileSync(configPath, JSON.stringify(config, null, 2), "utf8");
+
+console.log("✅ Updated", configPath);
+
+//-----------------------------------------------------------------------------
+// Final notice
+//-----------------------------------------------------------------------------
+
+console.log("\nIMPORTANT!!!!!");
+console.log(
+ "This script does NOT update the release-please.yml workflow file.",
+);
+console.log("You must do that manually.");