Skip to content
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

Refactor and add some test #9

Merged
merged 13 commits into from
May 13, 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
22 changes: 16 additions & 6 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,25 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
with:
submodules: 'true'
- uses: actions/setup-node@v1
submodules: "true"

- uses: actions/setup-node@v3
with:
node-version: 14
node-version-file: ".node-version"
registry-url: https://registry.npmjs.org/
- run: yarn install --frozen-lockfile
- run: yarn version --new-version "${GITHUB_REF:11}" --no-git-tag-version
cache: "yarn"

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

- name: Build
run: yarn build

- name: Test
run: yarn test

- run: yarn publish --access public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
29 changes: 29 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: PR

on:
pull-request:

jobs:
test:
name: Test
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
with:
submodules: "true"

- uses: actions/setup-node@v3
with:
node-version-file: ".node-version"
registry-url: https://registry.npmjs.org/
cache: "yarn"

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

- name: Build
run: yarn build

- name: Test
run: yarn test
1 change: 1 addition & 0 deletions .node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v18.14.0
13 changes: 10 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
],
"scripts": {
"build": "yarn build:icon",
"build:icon": "node src/build.js",
"prepublishOnly": "yarn build"
"build:icon": "node scripts/build.js",
"test": "vitest run"
},
"repository": {
"type": "git",
Expand All @@ -38,9 +38,16 @@
"svelte": ">=3"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.2",
"@testing-library/svelte": "^3.2.2",
"fs-extra": "^9.0.1",
"html-minifier-terser": "^5.1.1",
"jsdom": "^21.1.0",
"log-update": "^4.0.0",
"svelte": "^3.0.0"
"memfs": "^3.4.13",
"p-map": "^5.5.0",
"svelte": "^3.55.1",
"svelte-htm": "^1.2.0",
"vitest": "^0.28.4"
}
}
131 changes: 131 additions & 0 deletions scripts/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import fs from "fs-extra";
import logUpdate from "log-update";
import pMap, { pMapSkip } from "p-map";
import path from "path";
import { componentTemplate, definitionsTemplate } from "./template.js";
import {
generateIconName,
getCurrentDirname,
getIcons,
getWeights,
readSVG,
} from "./utils.js";

const isTTY = process.stdout.isTTY;
const __dirname = getCurrentDirname();

const rootDir = path.resolve(__dirname, "..");
const outputDir = path.join(rootDir, "lib");
const assetsDir = path.join(rootDir, "core", "assets");

/** @type {string[]} */
let progress = [];

/** @param {string} str */
function logProgress(str) {
if (isTTY) {
progress.push(str);
logUpdate(progress.join("\n"));
} else {
console.log(str);
}

return {
done: () => {
if (isTTY) progress = progress.filter((p) => p !== str);
},
};
}

/**
*
* @param {string} icon - icon file name, eg. activity.svg
* @param {string[]} weightVariants - all icon weights
*/
export async function generateComponents(icon, weightVariants) {
try {
const p = logProgress(`Generating ${icon}...`);
const iconName = icon.slice(0, -4); // activity.svg -> activity

const iconWeights = await pMap(weightVariants, async (weight) => {
let fileName = iconName;
if (weight !== "regular") fileName += `-${weight}`;

const svgPath = await readSVG(
path.join(assetsDir, weight, `${fileName}.svg`)
);

return {
weight,
svgPath,
};
});

let componentString = componentTemplate(iconWeights);
let componentName = generateIconName(iconName);

const cmpDir = path.join(outputDir, componentName);
await fs.ensureDir(cmpDir);
await fs.writeFile(
path.join(cmpDir, `${componentName}.svelte`),
componentString
);
await fs.writeFile(
path.join(cmpDir, "index.js"),
`import ${componentName} from "./${componentName}.svelte"\nexport default ${componentName};`
);
await fs.writeFile(
path.join(cmpDir, "index.d.ts"),
`export { ${componentName} as default } from "../";\n`
);

p.done();
return {
iconName: icon,
name: componentName,
weights: iconWeights,
};
} catch (e) {
return pMapSkip;
}
}

export async function main() {
let concurrency = 5;

const weights = await getWeights(assetsDir);
const regularIcons = await getIcons(assetsDir, "regular");

await fs.remove(outputDir);
await fs.copy(path.join(rootDir, "src", "lib"), outputDir);

const components = await pMap(
regularIcons,
(icon) => generateComponents(icon, weights),
{
concurrency,
}
);

const indexString = components.map(
(cmp) => `export { default as ${cmp.name} } from './${cmp.name}';`
);
indexString.unshift(
"export { default as IconContext } from './IconContext';\n"
);

const definitionsString = definitionsTemplate(components);

await fs.writeFile(path.join(outputDir, "index.js"), indexString.join("\n"));
await fs.writeFile(path.join(outputDir, "index.d.ts"), definitionsString);

if (isTTY) {
logUpdate.clear();
logUpdate.done();
}

const passes = components.length;
console.log(`✔ ${passes} component${passes > 1 ? "s" : ""} generated`);
}

if (process.env.NODE_ENV !== "test") main();
79 changes: 79 additions & 0 deletions scripts/template.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
*
* @param {{ weight: string, svgPath: string }[]} iconWeights
* @returns
*/
export function componentTemplate(iconWeights) {
let componentString = `<!-- GENERATED FILE -->
<script>
import { getContext } from "svelte";

const {
weight: ctxWeight,
color: ctxColor,
size: ctxSize,
mirrored: ctxMirrored,
...restCtx
} = getContext("iconCtx") || {};

export let weight = ctxWeight ?? "regular";
export let color = ctxColor ?? "currentColor";
export let size = ctxSize ?? "1em";
export let mirrored = ctxMirrored || false;
</script>

<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
fill={color}
transform={mirrored ? "scale(-1, 1)" : undefined}
viewBox="0 0 256 256"
{...restCtx}
{...$$restProps}>
<slot/>
<rect width="256" height="256" fill="none" />
${iconWeights
.map(({ weight, svgPath }, i) => {
const cond =
i === 0
? `{#if weight === "${weight}"}`
: `{:else if weight === "${weight}"}`;
return ` ${cond}\n ${svgPath.trim()}\n`;
})
.join("")} {:else}
{(console.error('Unsupported icon weight. Choose from "thin", "light", "regular", "bold", "fill", or "duotone".'), "")}
{/if}
</svg>`;

return componentString;
}

/**
*
* @param {{
* name: string,
* iconName: string,
* weights: {
* svgPath: string,
* weight: string
* }[]
* }[]} components
* @returns
*/
export function definitionsTemplate(components) {
return `import { SvelteComponent, IconProps } from "./shared";

export interface IconContextProps {
values: IconProps;
}

export declare class IconContext extends SvelteComponent<IconContextProps> {}

${components
.map(
(cmp) =>
`export declare class ${cmp.name} extends SvelteComponent<IconProps> {}`
)
.join("\n")}`;
}
17 changes: 12 additions & 5 deletions scripts/testRegex.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { readFileSync } from "fs";
import { getWeights, getIcons, ASSETS_PATH } from "../src/utils";
import fs from "fs-extra";
import { resolve } from "path";

const { readFileSync, readdirSync } = fs;

(() => {
const weights = getWeights();
const weights = readdirSync(resolve("phosphor-icons", "assets"), {
withFileTypes: true,
})
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);
let patterns = {
xmlTag: { pattern: /^.*<\?xml.*?>/g, match: 0 },
svgTagOpen: { pattern: /<svg.*?>/g, match: 0 },
Expand All @@ -17,10 +22,12 @@ import { resolve } from "path";
};

for (const weight of weights) {
const iconsOfWeight = getIcons(weight);
const iconsOfWeight = readdirSync(
resolve("phosphor-icons", "assets", weight)
);
for (const iconFileName of iconsOfWeight) {
const rawSVG = readFileSync(
resolve(ASSETS_PATH, weight, iconFileName),
resolve("phosphor-icons", "assets", weight, iconFileName),
"utf-8"
);
for (const [patternName, { pattern }] of Object.entries(patterns)) {
Expand Down
Loading