@octaviaflow/icon-build-helpers 1.0.0
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/README.md +50 -0
- package/package.json +55 -0
- package/src/builders/index.js +33 -0
- package/src/builders/plugins/virtual.js +51 -0
- package/src/builders/react/builder.js +313 -0
- package/src/builders/react/components/CarbonIcon.d.ts +18 -0
- package/src/builders/react/components/Icon.tsx +87 -0
- package/src/builders/react/next/babel.js +37 -0
- package/src/builders/react/next/convert.js +107 -0
- package/src/builders/react/next/templates.js +71 -0
- package/src/builders/react/next/typescript.js +87 -0
- package/src/builders/react/next.js +382 -0
- package/src/builders/svg.js +28 -0
- package/src/builders/vanilla.js +115 -0
- package/src/index.js +21 -0
- package/src/metadata/README.md +37 -0
- package/src/metadata/adapters/index.js +34 -0
- package/src/metadata/adapters/memory.js +55 -0
- package/src/metadata/adapters/yml.js +92 -0
- package/src/metadata/extension.js +92 -0
- package/src/metadata/extensions/assets.js +45 -0
- package/src/metadata/extensions/categories.js +141 -0
- package/src/metadata/extensions/deprecated.js +73 -0
- package/src/metadata/extensions/icons.js +152 -0
- package/src/metadata/extensions/index.js +48 -0
- package/src/metadata/extensions/module-info.js +154 -0
- package/src/metadata/extensions/module-name.js +54 -0
- package/src/metadata/extensions/output/getModuleName.js +52 -0
- package/src/metadata/extensions/output/index.js +251 -0
- package/src/metadata/extensions/output/optimizer.js +226 -0
- package/src/metadata/extensions/pictograms.js +85 -0
- package/src/metadata/index.js +178 -0
- package/src/metadata/migrations/2020-01-27.js +78 -0
- package/src/metadata/migrations/2020-02-17-remove-usage-fields.js +45 -0
- package/src/metadata/migrations/2020-02-17-update-pictogram-files.js +79 -0
- package/src/metadata/migrations/README.md +12 -0
- package/src/metadata/storage.js +68 -0
- package/src/metadata/validate.js +54 -0
- package/src/registry.js +158 -0
- package/src/tools.js +18 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright OctaviaFlow
|
|
3
|
+
* Author: Vishal Kumar
|
|
4
|
+
* Created: 11/November/2025
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Copyright IBM Corp. 2020, 2023
|
|
12
|
+
*
|
|
13
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
14
|
+
* LICENSE file in the root directory of this source tree.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Use the provided file adapter and directory information to load the given
|
|
21
|
+
* set of extensions.
|
|
22
|
+
* @param {Adapter} adapter
|
|
23
|
+
* @param {string} directory
|
|
24
|
+
* @param {Array<Extension>} [extensions]
|
|
25
|
+
* @returns {Array<Extension>}
|
|
26
|
+
*/
|
|
27
|
+
function load(adapter, directory, extensions = []) {
|
|
28
|
+
return Promise.all(
|
|
29
|
+
extensions.map(async (extension) => {
|
|
30
|
+
// If computed, the extension has no file that has been persisted to disk
|
|
31
|
+
// so we don't have to load it.
|
|
32
|
+
if (extension.computed) {
|
|
33
|
+
return extension;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const data = await adapter.read(directory, extension.name);
|
|
37
|
+
return {
|
|
38
|
+
...extension,
|
|
39
|
+
data,
|
|
40
|
+
};
|
|
41
|
+
})
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Use the provided file adapter and directory information to save the given
|
|
47
|
+
* set of extensions.
|
|
48
|
+
* @param {Adapter} adapter
|
|
49
|
+
* @param {string} directory
|
|
50
|
+
* @param {Array<Extension>} [extensions]
|
|
51
|
+
* @returns {Array<Extension>}
|
|
52
|
+
*/
|
|
53
|
+
function save(adapter, directory, extensions = []) {
|
|
54
|
+
return Promise.all(
|
|
55
|
+
extensions.map((extension) => {
|
|
56
|
+
// If the extension is computed, there is nothing to persist to disk
|
|
57
|
+
if (extension.computed) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
return adapter.write(directory, extension.name, extension.data);
|
|
61
|
+
})
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = {
|
|
66
|
+
load,
|
|
67
|
+
save,
|
|
68
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright OctaviaFlow
|
|
3
|
+
* Author: Vishal Kumar
|
|
4
|
+
* Created: 11/November/2025
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Copyright IBM Corp. 2020, 2023
|
|
12
|
+
*
|
|
13
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
14
|
+
* LICENSE file in the root directory of this source tree.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const { reporter } = require('@octaviaflow/cli-reporter');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Validate the given icons and extension metadata against the asset registry
|
|
23
|
+
* and the base icon schema, alongside running custom validators for each
|
|
24
|
+
* extension. This is a useful check to guarantee that icons exist in both the
|
|
25
|
+
* source directory of SVG assets and in metadata and corresponding extensions.
|
|
26
|
+
* @param {Registry} registry
|
|
27
|
+
* @param {Array<Extension>} [extensions]
|
|
28
|
+
* @returns {void}
|
|
29
|
+
*/
|
|
30
|
+
function validate(registry, extensions = []) {
|
|
31
|
+
for (const extension of extensions) {
|
|
32
|
+
if (extension.schema) {
|
|
33
|
+
const { error, value } = extension.schema.validate(extension.data);
|
|
34
|
+
if (error) {
|
|
35
|
+
const failedAssets = error.details.map(({ path, message }) => ({
|
|
36
|
+
index: path[0],
|
|
37
|
+
message,
|
|
38
|
+
}));
|
|
39
|
+
reporter.error(`Unable to validate the ${extension.name} extension:`);
|
|
40
|
+
failedAssets.forEach((asset) => {
|
|
41
|
+
reporter.error(`Error: ${asset.message}`);
|
|
42
|
+
reporter.info(JSON.stringify(value[asset.index], null, 2));
|
|
43
|
+
});
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (extension.validate) {
|
|
49
|
+
extension.validate(registry, extension.data);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = validate;
|
package/src/registry.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright OctaviaFlow
|
|
3
|
+
* Author: Vishal Kumar
|
|
4
|
+
* Created: 11/November/2025
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Copyright IBM Corp. 2020, 2023
|
|
12
|
+
*
|
|
13
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
14
|
+
* LICENSE file in the root directory of this source tree.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const fs = require('fs-extra');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A collection of icons built up from a source directory of .svg assets
|
|
24
|
+
* @typedef {Map<string, Icon>} Registry
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* An icon is defined by a unique name in "<name>.svg" that can have an optional
|
|
29
|
+
* namespace and has multiple assets that detail the assets available in a
|
|
30
|
+
* source directory of .svg assets
|
|
31
|
+
* @typedef {object} Icon
|
|
32
|
+
* @property {string} id
|
|
33
|
+
* @property {Array<string>} namespace
|
|
34
|
+
* @property {Array<Asset>} assets
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* An asset for an icon that details its size and filepath information
|
|
39
|
+
* @typedef {object} Asset
|
|
40
|
+
* @property {(number|('glyph'))} size
|
|
41
|
+
* @property {string} filepath
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Create a registry of icons from the assets found within the given directory
|
|
46
|
+
* @param {string} directory
|
|
47
|
+
* @returns {Registry}
|
|
48
|
+
*/
|
|
49
|
+
async function create(directory) {
|
|
50
|
+
const registry = new Map();
|
|
51
|
+
const queue = await getFilepathsFromDirectory(directory);
|
|
52
|
+
|
|
53
|
+
// Our queue is built up with filepaths that we need to process. Each
|
|
54
|
+
// filepath can either be a directory or an asset corresponding to an icon.
|
|
55
|
+
while (queue.length > 0) {
|
|
56
|
+
const filepath = queue.shift();
|
|
57
|
+
const stats = await fs.stat(filepath);
|
|
58
|
+
|
|
59
|
+
// If we encounter a directory, then we append all of the filepaths we've
|
|
60
|
+
// found to the queue to be processed
|
|
61
|
+
if (await stats.isDirectory()) {
|
|
62
|
+
const filepaths = await getFilepathsFromDirectory(filepath);
|
|
63
|
+
queue.push(...filepaths);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// When we have an asset for an icon, we need to build up information about
|
|
68
|
+
// it and correctly assign, or update, the entry for this icon in the
|
|
69
|
+
// registry.
|
|
70
|
+
|
|
71
|
+
// We want to build up an array of the relative path from the SVG folder to
|
|
72
|
+
// the asset that we've found. This relative path may contain size or
|
|
73
|
+
// namespace information.
|
|
74
|
+
const directories = path
|
|
75
|
+
.relative(directory, path.dirname(filepath))
|
|
76
|
+
.split(path.sep)
|
|
77
|
+
.filter(Boolean);
|
|
78
|
+
|
|
79
|
+
// Our namespace is generated from every directory that is not a size
|
|
80
|
+
const namespace = directories.filter((directory) => isNaN(directory));
|
|
81
|
+
const asset = {
|
|
82
|
+
id: path.basename(filepath, '.svg'),
|
|
83
|
+
filepath,
|
|
84
|
+
namespace,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Our size folder is generated from the first directory that is a number
|
|
88
|
+
const sizeFolderName = directories.find((directory) => !isNaN(directory));
|
|
89
|
+
if (sizeFolderName) {
|
|
90
|
+
asset.size = parseInt(sizeFolderName, 10);
|
|
91
|
+
} else {
|
|
92
|
+
asset.size = 'glyph';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!registry.has(asset.id)) {
|
|
96
|
+
registry.set(asset.id, {
|
|
97
|
+
id: asset.id,
|
|
98
|
+
namespace: asset.namespace,
|
|
99
|
+
assets: [],
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const entry = registry.get(asset.id);
|
|
104
|
+
|
|
105
|
+
// We have an invariant that all icons in a source SVG folder must have a
|
|
106
|
+
// unique name even if they are under different namespaces.
|
|
107
|
+
if (hash(entry.id, entry.namespace) !== hash(asset.id, asset.namespace)) {
|
|
108
|
+
const expected = entry.namespace.join(', ');
|
|
109
|
+
const actual = asset.namespace.join(', ');
|
|
110
|
+
throw new Error(
|
|
111
|
+
`Found namespace mismatch with asset ${asset.id}. Expected ` +
|
|
112
|
+
`[${expected}] but received [${actual}]. This likely means that ` +
|
|
113
|
+
`there is a duplicate asset in the source SVG folder`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
entry.assets.push({
|
|
118
|
+
filepath: asset.filepath,
|
|
119
|
+
size: asset.size,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return registry;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Generate a hash with the basename and namespace of an asset to compare if two
|
|
128
|
+
* assets in the registry are equivalent
|
|
129
|
+
* @param {string} basename
|
|
130
|
+
* @param {Array<string>} [namespace]
|
|
131
|
+
* @returns {string}
|
|
132
|
+
*/
|
|
133
|
+
function hash(basename, namespace = []) {
|
|
134
|
+
return [...namespace, basename].join('/');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const denylist = new Set(['.DS_Store']);
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get all the filepaths from the given directory that are not contained in a
|
|
141
|
+
* denylist.
|
|
142
|
+
* @param {string} directory
|
|
143
|
+
* @returns {Array<string>}
|
|
144
|
+
*/
|
|
145
|
+
async function getFilepathsFromDirectory(directory) {
|
|
146
|
+
const files = await fs.readdir(directory);
|
|
147
|
+
return files
|
|
148
|
+
.filter((name) => {
|
|
149
|
+
return !denylist.has(name);
|
|
150
|
+
})
|
|
151
|
+
.map((filename) => {
|
|
152
|
+
return path.join(directory, filename);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = {
|
|
157
|
+
create,
|
|
158
|
+
};
|
package/src/tools.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright OctaviaFlow
|
|
3
|
+
* Author: Vishal Kumar
|
|
4
|
+
* Created: 11/November/2025
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async function flatMapAsync(source, mapFn) {
|
|
12
|
+
const results = await Promise.all(source.map(mapFn));
|
|
13
|
+
return results.reduce((acc, result) => acc.concat(result), []);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
flatMapAsync,
|
|
18
|
+
};
|