@phill-component/icons 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +33 -0
- package/scripts/build.js +136 -0
- package/scripts/install.js +123 -0
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@phill-component/icons",
|
|
3
|
+
"version": "0.1.6",
|
|
4
|
+
"description": "Unified icons for phillUI mobile and pc libraries",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"scripts"
|
|
10
|
+
],
|
|
11
|
+
"bin": {
|
|
12
|
+
"phillui-icons": "scripts/install.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "node scripts/build.js",
|
|
16
|
+
"release-core-icons": "node ../../../scripts/publish-icons.js"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"icons",
|
|
20
|
+
"vue",
|
|
21
|
+
"uniapp"
|
|
22
|
+
],
|
|
23
|
+
"author": "",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"registry": "https://registry.npmjs.org/"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"svgo": "^4.0.0",
|
|
30
|
+
"svgtofont": "^6.5.1",
|
|
31
|
+
"case": "^1.6.3"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/scripts/build.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
const svgtofont = require('svgtofont').default;
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const { optimize } = require('svgo');
|
|
5
|
+
const Case = require('case');
|
|
6
|
+
|
|
7
|
+
const rootDir = path.resolve(__dirname, '..');
|
|
8
|
+
const srcDir = path.resolve(rootDir, 'svg');
|
|
9
|
+
const distDir = path.resolve(rootDir, 'dist');
|
|
10
|
+
const vueDir = path.resolve(distDir, 'vue');
|
|
11
|
+
const uniappDir = path.resolve(distDir, 'uniapp');
|
|
12
|
+
const uniappCompDir = path.resolve(distDir, 'uniapp-components');
|
|
13
|
+
const uniappImgDir = path.resolve(uniappDir, 'images');
|
|
14
|
+
|
|
15
|
+
// Ensure directories exist
|
|
16
|
+
[distDir, vueDir, uniappDir, uniappCompDir, uniappImgDir].forEach(dir => {
|
|
17
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
function isMultiColor(svgContent) {
|
|
21
|
+
const colorRegex = /(fill|stroke)="(?!none|currentColor|\$)(#[0-9a-fA-F]{3,6}|rgba?|hsla?)/gi;
|
|
22
|
+
const matches = svgContent.match(colorRegex);
|
|
23
|
+
if (!matches) return false;
|
|
24
|
+
const colors = new Set(matches.map(m => m.split('"')[1].toLowerCase()));
|
|
25
|
+
return colors.size > 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function build() {
|
|
29
|
+
const files = fs.readdirSync(srcDir).filter(f => f.endsWith('.svg'));
|
|
30
|
+
const singleColorIcons = [];
|
|
31
|
+
const multiColorIcons = [];
|
|
32
|
+
const iconData = [];
|
|
33
|
+
const svgMappings = {};
|
|
34
|
+
|
|
35
|
+
console.log(`Building icons from ${srcDir} to ${distDir}...`);
|
|
36
|
+
|
|
37
|
+
for (const file of files) {
|
|
38
|
+
const name = file.replace('.svg', '');
|
|
39
|
+
const pascalName = Case.pascal(name);
|
|
40
|
+
const content = fs.readFileSync(path.join(srcDir, file), 'utf-8');
|
|
41
|
+
|
|
42
|
+
const multi = isMultiColor(content);
|
|
43
|
+
|
|
44
|
+
// Optimize
|
|
45
|
+
const svgoPlugins = ['preset-default', 'removeDimensions'];
|
|
46
|
+
if (!multi) {
|
|
47
|
+
svgoPlugins.push({
|
|
48
|
+
name: 'convertColors',
|
|
49
|
+
params: { currentColor: true }
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let optimized = optimize(content, {
|
|
54
|
+
path: file,
|
|
55
|
+
plugins: svgoPlugins
|
|
56
|
+
}).data;
|
|
57
|
+
|
|
58
|
+
const innerSvg = optimized.replace(/<svg[^>]*>|<\/svg>/g, '');
|
|
59
|
+
|
|
60
|
+
// 1. Generate Standard Vue Component (PC/Web)
|
|
61
|
+
const vueContent = `<template>
|
|
62
|
+
<svg
|
|
63
|
+
viewBox="0 0 24 24"
|
|
64
|
+
:width="size"
|
|
65
|
+
:height="size"
|
|
66
|
+
:fill="multi ? 'none' : color"
|
|
67
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
68
|
+
v-bind="$attrs"
|
|
69
|
+
>
|
|
70
|
+
${innerSvg}
|
|
71
|
+
</svg>
|
|
72
|
+
</template>
|
|
73
|
+
|
|
74
|
+
<script setup>
|
|
75
|
+
defineProps({
|
|
76
|
+
size: { type: [String, Number], default: '1em' },
|
|
77
|
+
color: { type: String, default: 'currentColor' },
|
|
78
|
+
multi: { type: Boolean, default: ${multi} }
|
|
79
|
+
});
|
|
80
|
+
</script>`;
|
|
81
|
+
fs.writeFileSync(path.join(vueDir, `${pascalName}.vue`), vueContent);
|
|
82
|
+
|
|
83
|
+
// 2. Optimized Standalone SVG for Native/MP
|
|
84
|
+
if (multi) {
|
|
85
|
+
multiColorIcons.push(name);
|
|
86
|
+
// Include multi-color SVG data inline in icons-svg.js
|
|
87
|
+
// This avoids the need for dynamic imports which don't work with bare module specifiers
|
|
88
|
+
svgMappings[name] = { inner: innerSvg, multi: true };
|
|
89
|
+
|
|
90
|
+
fs.writeFileSync(path.join(uniappImgDir, file), optimized);
|
|
91
|
+
} else {
|
|
92
|
+
singleColorIcons.push(file);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
iconData.push({ name, pascalName, multi });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 3. Generate Vue Entry
|
|
99
|
+
const entryContent = iconData.map(i => `export { default as Icon${i.pascalName} } from './vue/${i.pascalName}.vue';`).join('\n');
|
|
100
|
+
fs.writeFileSync(path.join(distDir, 'index.js'), entryContent);
|
|
101
|
+
|
|
102
|
+
// 4. Build Native Font (UniApp Native Single-color)
|
|
103
|
+
let fontMappings = {};
|
|
104
|
+
if (singleColorIcons.length > 0) {
|
|
105
|
+
const tempFontSrc = path.join(rootDir, 'temp-font-src');
|
|
106
|
+
if (!fs.existsSync(tempFontSrc)) fs.mkdirSync(tempFontSrc);
|
|
107
|
+
singleColorIcons.forEach(f => fs.copyFileSync(path.join(srcDir, f), path.join(tempFontSrc, f)));
|
|
108
|
+
|
|
109
|
+
await svgtofont({
|
|
110
|
+
src: tempFontSrc,
|
|
111
|
+
dist: uniappDir,
|
|
112
|
+
fontName: 'upicon-custom',
|
|
113
|
+
css: false,
|
|
114
|
+
outSVGReact: false,
|
|
115
|
+
outSVGPath: false,
|
|
116
|
+
typescript: true,
|
|
117
|
+
emptyDist: false,
|
|
118
|
+
getIconUnicode: (name, unicode) => {
|
|
119
|
+
fontMappings[name] = unicode;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
fs.rmSync(tempFontSrc, { recursive: true, force: true });
|
|
123
|
+
console.log('Native font generated.');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 5. Generate Mappings for consumers
|
|
127
|
+
fs.writeFileSync(path.join(uniappDir, 'icons-svg.js'), `export default ${JSON.stringify(svgMappings, null, 2)}`);
|
|
128
|
+
fs.writeFileSync(path.join(uniappDir, 'icons-generated.js'), `export default ${JSON.stringify(fontMappings, null, 2)}`);
|
|
129
|
+
fs.writeFileSync(path.join(uniappDir, 'icons-generated.uts'), `export default ${JSON.stringify(fontMappings, null, 2)} as UTSJSONObject`);
|
|
130
|
+
fs.writeFileSync(path.join(uniappDir, 'icons-custom.json'), JSON.stringify(iconData.map(i => i.name), null, 2));
|
|
131
|
+
fs.writeFileSync(path.join(uniappDir, 'icons-multicolor.json'), JSON.stringify(multiColorIcons, null, 2));
|
|
132
|
+
|
|
133
|
+
console.log('All icons built to dist successfully.');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
build().catch(console.error);
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Install phillui-icons into uni_modules for UniApp projects.
|
|
7
|
+
* - Detects project root (INIT_CWD preferred) and whether 'src' exists.
|
|
8
|
+
* - Copies this package's dist assets into <project>/(src/)?uni_modules/phillui-icons/dist
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
function resolvePackageRoot() {
|
|
12
|
+
// When executed from node_modules: __dirname -> <pkg>/scripts
|
|
13
|
+
// In workspace/dev: __dirname -> repo/packages/core/icons/scripts
|
|
14
|
+
const scriptsDir = __dirname;
|
|
15
|
+
const candidate = path.resolve(scriptsDir, '..'); // <pkg>
|
|
16
|
+
if (fs.existsSync(path.join(candidate, 'dist'))) return candidate;
|
|
17
|
+
const alt = path.resolve(scriptsDir, '../..'); // in case of different layouts
|
|
18
|
+
if (fs.existsSync(path.join(alt, 'dist'))) return alt;
|
|
19
|
+
throw new Error('Cannot locate phillui-icons package root (dist not found).');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function copyDir(src, dest, exclude = []) {
|
|
23
|
+
if (!fs.existsSync(src)) return;
|
|
24
|
+
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
|
25
|
+
for (const entry of fs.readdirSync(src)) {
|
|
26
|
+
if (exclude.some(pattern => {
|
|
27
|
+
if (pattern.startsWith('*.')) return entry.endsWith(pattern.slice(1));
|
|
28
|
+
return entry === pattern;
|
|
29
|
+
})) continue;
|
|
30
|
+
|
|
31
|
+
const s = path.join(src, entry);
|
|
32
|
+
const d = path.join(dest, entry);
|
|
33
|
+
const stat = fs.lstatSync(s);
|
|
34
|
+
if (stat.isDirectory()) {
|
|
35
|
+
copyDir(s, d, exclude);
|
|
36
|
+
} else if (stat.isSymbolicLink()) {
|
|
37
|
+
const real = fs.realpathSync(s);
|
|
38
|
+
const realStat = fs.statSync(real);
|
|
39
|
+
if (realStat.isDirectory()) {
|
|
40
|
+
copyDir(real, d, exclude);
|
|
41
|
+
} else {
|
|
42
|
+
fs.copyFileSync(real, d);
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
fs.copyFileSync(s, d);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function rimraf(p) {
|
|
51
|
+
if (fs.existsSync(p)) fs.rmSync(p, { recursive: true, force: true });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function main() {
|
|
55
|
+
const projectRoot = process.env.INIT_CWD || process.cwd();
|
|
56
|
+
const srcDir = path.join(projectRoot, 'src');
|
|
57
|
+
const uniModulesBase = fs.existsSync(srcDir)
|
|
58
|
+
? path.join(srcDir, 'uni_modules')
|
|
59
|
+
: path.join(projectRoot, 'uni_modules');
|
|
60
|
+
const targetPkgDir = path.join(uniModulesBase, 'phillui-icons');
|
|
61
|
+
const targetDistDir = path.join(targetPkgDir, 'dist');
|
|
62
|
+
|
|
63
|
+
const pkgRoot = resolvePackageRoot();
|
|
64
|
+
const sourceDistDir = path.join(pkgRoot, 'dist');
|
|
65
|
+
|
|
66
|
+
const isX = fs.existsSync(path.join(projectRoot, 'App.uvue')) || fs.existsSync(path.join(srcDir, 'App.uvue'));
|
|
67
|
+
const exclude = isX ? [] : ['*.uvue', '*.uts'];
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
if (!fs.existsSync(uniModulesBase)) fs.mkdirSync(uniModulesBase, { recursive: true });
|
|
71
|
+
// Clean old content
|
|
72
|
+
rimraf(targetPkgDir);
|
|
73
|
+
fs.mkdirSync(targetPkgDir, { recursive: true });
|
|
74
|
+
// Only copy dist (uniapp and vue build outputs)
|
|
75
|
+
copyDir(sourceDistDir, targetDistDir, exclude);
|
|
76
|
+
// Lightweight package.json for uni_modules (optional but helps tooling)
|
|
77
|
+
const lightPkgJson = {
|
|
78
|
+
name: 'phillui-icons',
|
|
79
|
+
description: 'Icons assets for phillUI in UniApp uni_modules',
|
|
80
|
+
};
|
|
81
|
+
fs.writeFileSync(path.join(targetPkgDir, 'package.json'), JSON.stringify(lightPkgJson, null, 2));
|
|
82
|
+
console.log(`[phillui/icons] Installed to ${targetDistDir} ${isX ? '(UniApp X)' : '(UniApp, Exclude .uvue/.uts)'}`);
|
|
83
|
+
|
|
84
|
+
// Simplified: No longer patching vite.config as we use relative paths
|
|
85
|
+
// patchViteAlias(projectRoot, !!fs.existsSync(srcDir));
|
|
86
|
+
} catch (e) {
|
|
87
|
+
console.error('[phillui/icons] Installation failed:', e.message);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function patchViteAlias(projectRoot, hasSrc) {
|
|
93
|
+
const candidates = ['vite.config.ts', 'vite.config.js'].map(f => path.join(projectRoot, f));
|
|
94
|
+
const vitePath = candidates.find(p => fs.existsSync(p));
|
|
95
|
+
if (!vitePath) return;
|
|
96
|
+
try {
|
|
97
|
+
let code = fs.readFileSync(vitePath, 'utf8');
|
|
98
|
+
if (code.includes('/* phillui-icons-alias */') || code.includes('phillui-icons')) {
|
|
99
|
+
return; // already patched
|
|
100
|
+
}
|
|
101
|
+
const replacementPath = hasSrc ? 'path.resolve(__dirname, \'src/uni_modules/phillui-icons\')'
|
|
102
|
+
: 'path.resolve(__dirname, \'uni_modules/phillui-icons\')';
|
|
103
|
+
// Ensure path import
|
|
104
|
+
if (!/import\s+path\s+from\s+['"]path['"]/.test(code)) {
|
|
105
|
+
code = code.replace(/import\s+uni\s+from\s+['"]@dcloudio\/vite-plugin-uni['"]\s*;?/, (m) => `${m}\nimport path from "path";`);
|
|
106
|
+
}
|
|
107
|
+
// Insert alias entry
|
|
108
|
+
if (/alias\s*:\s*\[/.test(code)) {
|
|
109
|
+
code = code.replace(/alias\s*:\s*\[/, (m) => `${m}\n /* phillui-icons-alias */ { find: /phillui-icons/, replacement: ${replacementPath} },`);
|
|
110
|
+
} else if (/resolve\s*:\s*\{/.test(code)) {
|
|
111
|
+
code = code.replace(/resolve\s*:\s*\{/, (m) => `${m}\n alias: [\n /* phillui-icons-alias */ { find: /phillui-icons/, replacement: ${replacementPath} }\n ],`);
|
|
112
|
+
} else {
|
|
113
|
+
// Fallback: inject a resolve block after plugins
|
|
114
|
+
code = code.replace(/plugins\s*:\s*\[[\s\S]*?\],/, (m) => `${m}\n resolve: {\n alias: [\n /* phillui-icons-alias */ { find: /phillui-icons/, replacement: ${replacementPath} }\n ]\n },`);
|
|
115
|
+
}
|
|
116
|
+
fs.writeFileSync(vitePath, code, 'utf8');
|
|
117
|
+
console.log('[phillui/icons] Patched Vite alias for phillui-icons.');
|
|
118
|
+
} catch (e) {
|
|
119
|
+
console.warn('[phillui/icons] Skip patching Vite alias:', e.message);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
main();
|