Skip to content

Commit

Permalink
Add support for renamed icons & Lucide-prefixed, add auto-updater (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
WarningImHack3r authored Sep 15, 2024
1 parent 847851a commit 55efd5a
Show file tree
Hide file tree
Showing 10 changed files with 350 additions and 23 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/auto-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Auto Release

on:
push:
branches:
- main

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
create-gh-release:
name: Create GitHub Release
runs-on: ubuntu-latest
if: github.event.commits[0].committer.name == 'github-actions[bot]' && startsWith(github.event.head_commit.message, '[release]')
permissions:
contents: write

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Get tag
id: get_tag
run: echo "tag=$(jq -r --arg prefix "v" '$prefix + .version' package.json)" >> "$GITHUB_OUTPUT"

- name: Get Lucide version
id: get_lucide_version
run: echo "lucide_version=$(jq -r '.devDependencies["lucide-svelte"] | ltrimstr("^")' package.json)" >> "$GITHUB_OUTPUT"

- name: Create release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.get_tag.outputs.tag }}
body: |
- Add support for newly renamed components in Lucide v${{ steps.get_lucide_version.outputs.lucide_version }}
token: ${{ secrets.WORKFLOW_PAT }}
70 changes: 67 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ on:
- main
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
name: Build
Expand All @@ -18,7 +22,7 @@ jobs:
- name: Set up PNPM
uses: pnpm/action-setup@v4

- name: Use Node.js
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: latest
Expand All @@ -42,7 +46,7 @@ jobs:
- name: Set up PNPM
uses: pnpm/action-setup@v4

- name: Use Node.js
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: latest
Expand All @@ -69,7 +73,7 @@ jobs:
- name: Set up PNPM
uses: pnpm/action-setup@v4

- name: Use Node.js
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: latest
Expand All @@ -80,3 +84,63 @@ jobs:

- name: Test
run: pnpm test

test-fail:
name: Test Fail
runs-on: ubuntu-latest
needs: test
if: failure() && !cancelled() && needs.test.result == 'failure' && github.event_name == 'pull_request' && (github.event.pull_request.user.login == 'dependabot[bot]' || github.event.pull_request.user.login == 'renovate[bot]')
permissions:
contents: write

steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
token: ${{ secrets.WORKFLOW_PAT }}

- name: Set up PNPM
uses: pnpm/action-setup@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: latest
cache: pnpm

- name: Set up Bun # Bun is the only way I can run the script that generates the map with such an import statement with
uses: oven-sh/setup-bun@v2 # it's also the fastest runtime and is 100% compatible with Node sooo 🤷‍♂️

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Empty replacements file
# This is necessary because we want to regenerate the whole map
run: echo "export default {};" > src/renamedReplacements.js

- name: Create Vitest report
run: pnpm vitest --reporter=json --outputFile=out.json

- name: Replace the map
run: |
map=$(bun --bun run scripts/renamings-map-from-test.js)
echo "export default $(echo $map)" > src/renamedReplacements.js
- name: Run Prettier
run: pnpm prettier --write src/renamedReplacements.js

- name: Upgrade Lucide
run: pnpm up -L lucide-svelte

- name: Bump the version in package.json
run: |
# Use jq to bump the version in package.json
jq '.version |= (split(".") | .[2] = (. [2] | tonumber + 1) | join("."))' package.json | sponge package.json
- name: Commit and push the changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "[release] Bump replacements map"
file_pattern: src/renamedReplacements.js package.json pnpm-lock.yaml
branch: ${{ github.event.repository.default_branch }}
4 changes: 4 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ on:
release:
types: [prereleased, released]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
publish:
name: Publish
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ but I don't know if it will ever be implemented.
[I opened an issue](https://github.com/lucide-icons/lucide/issues/2295) on the `lucide-icons` repository to discuss
this.

## Auto-updater

As of version 1.1.0, the plugin includes an auto-updater.
It helps support the renamed and deprecated icons by automatically updating the plugin when necessary.

If you use a recently deprecated icon and can't make your build work, please wait for a few hours for the plugin
to update itself.
If the issue persists, please open an issue on this repository.

More information about the auto-updater can be found in #6.

## Installation

Install the plugin with your package manager of choice:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vite-plugin-lucide-preprocess",
"version": "1.0.4",
"version": "1.1.0",
"author": {
"url": "https://github.com/WarningImHack3r",
"name": "WarningImHack3r"
Expand Down
27 changes: 27 additions & 0 deletions scripts/renamings-map-from-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import tests from "../out.json";

const failingMap = tests.testResults
.map(testResult =>
testResult.assertionResults
.map(({ status, title, failureMessages }) => {
if (status === "passed" || !title.startsWith("Alias imports:") || !failureMessages.length)
return;
const assertionError = failureMessages[0]?.split("\n")[0];
if (!assertionError) return;
const assert = /'(\S+)'.*?'(\S+)'/.exec(assertionError);
if (!assert) return;
const [_, wrongModule, correctModule] = assert;
return { wrongModule, correctModule };
})
.filter(Boolean)
.flat()
)
.flat();

console.log(
"{\n" +
failingMap
.map(({ wrongModule, correctModule }) => `\t"${wrongModule}": "${correctModule}"`)
.join(",\n") +
"\n}"
);
29 changes: 16 additions & 13 deletions src/plugin.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import renamedReplacementsModules from "./renamedReplacements.js";

const ignoredPaths = ["/node_modules/", "/.svelte-kit/"];

/**
Expand All @@ -22,21 +24,22 @@ export function rawModulesToLists(rawModules) {
* Converts a PascalCase icon component name to a dashed string
* (e.g. `IconComp` to `icon-comp`)
* @param component {string} The PascalCase icon component name
* @return {string} The dashed icon component name
* @return {string} The dashed icon component name, or the renamed one if it exists
*/
export function iconCompToDashed(component) {
return (
component
.split(" ")[0] // take only the first part if there's an alias
// transform "IconComp" to "icon-comp"
.replace(/[A-Z\d]/g, (match, offset) => (offset > 0 ? "-" : "") + match.toLowerCase())
// fix for NxN icons
.replace(/(\d)x-(\d)/, (_, a, b) => `${a}x${b}`)
// exception for ClockNN
.replace(/clock-(\d)-(\d)/, (_, a, b) => `clock-${a}${b}`)
// remove the "Icon" suffix
.replace(/-icon$/, "")
);
const computedDashed = component
.split(" ")[0] // take only the first part if there's an alias
// transform "IconComp" to "icon-comp"
.replace(/[A-Z\d]/g, (match, offset) => (offset > 0 ? "-" : "") + match.toLowerCase())
// fix for NxN icons
.replace(/(\d)x-(\d)/, (_, a, b) => `${a}x${b}`)
// exception for ClockNN
.replace(/clock-(\d)-(\d)/, (_, a, b) => `clock-${a}${b}`)
// remove the Lucide prefix
.replace(/^lucide-/, "")
// remove the "Icon" suffix
.replace(/-icon$/, "");
return renamedReplacementsModules[computedDashed] ?? computedDashed;
}

export const importsMatcher = /^(\s*)import\s+\{([^}]*)}\s+from\s+(["'])lucide-(.*?)["'](.*)$/gm;
Expand Down
30 changes: 27 additions & 3 deletions src/plugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,13 +270,32 @@ const conversionRegex = /^export \{ default as (\S+) } from ["']\.\/(\S+).svelte

describe("Icon component name conversion", async () => {
const imports = await import("/node_modules/lucide-svelte/dist/icons/index.js?raw");
const modules = imports.default.matchAll(conversionRegex);
const aliasesImports = await import("/node_modules/lucide-svelte/dist/aliases.js?raw");

const modules = [...imports.default.matchAll(conversionRegex)];
const aliases = [...aliasesImports.default.matchAll(conversionRegex)]
.filter(
alias =>
!modules.some(m => m[1] === alias[1]) &&
!alias[1].startsWith("Lucide") &&
!alias[1].endsWith("Icon")
)
.map(alias => [alias[0], alias[1], alias[2].replace(/icons\//, "")]);
for (const module of modules) {
const component = module[1];
const dashedName = module[2];

test(`${component} -> ${dashedName}`, () => {
expect(iconCompToDashed(component)).toBe(dashedName);
test(`Regular imports: ${component} -> ${dashedName}`, () => {
expect(iconCompToDashed(component)).eq(dashedName);
});
}
for (const alias of aliases) {
const component = alias[1];
const dashedName = alias[2];

// Rename the condition in the renaming script if you change the name of that test(s)!
test(`Alias imports: ${component} -> ${dashedName}`, () => {
expect(iconCompToDashed(component)).eq(dashedName);
});
}

Expand All @@ -290,6 +309,11 @@ describe("Icon component name conversion", async () => {
const dashed = iconCompToDashed("Icon1Icon");
expect(dashed).toBe("icon-1");
});

test("Lucide* prefix", () => {
const dashed = iconCompToDashed("LucideIcon1");
expect(dashed).toBe("icon-1");
});
});

describe("End-to-end", () => {
Expand Down
Loading

0 comments on commit 55efd5a

Please sign in to comment.