-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathcem.config.mjs
More file actions
149 lines (130 loc) · 4.47 KB
/
Copy pathcem.config.mjs
File metadata and controls
149 lines (130 loc) · 4.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import { readFileSync } from 'node:fs';
import path from 'node:path';
import { expandTypesPlugin, getTsProgram } from 'cem-plugin-expanded-types';
/**
* Maps each `#` subpath alias to the source directory it points at, derived
* from the `imports` field so the two can never drift apart.
*
* @example '#internals' -> 'src/internals'
*/
const ALIAS_DIRECTORIES = new Map(
Object.entries(
JSON.parse(readFileSync('./package.json', 'utf8')).imports
).map(([key, target]) => [
key.split('/', 1)[0],
target.replace(/^\.\//, '').split('/*')[0],
])
);
const ALIASES = Array.from(ALIAS_DIRECTORIES.keys());
const aliasOf = (specifier) =>
ALIASES.find((alias) => specifier.startsWith(`${alias}/`));
/** '#internals/part-map.js' -> 'src/internals/part-map.js' */
const toSourcePath = (specifier, alias) =>
`${ALIAS_DIRECTORIES.get(alias)}${specifier.slice(alias.length)}`;
/**
* The analyzer has no notion of subpath imports. It resolves a declaration's
* origin by handing the raw specifier to `new URL(specifier, base)`, and a
* leading `#` parses as a URL *fragment* - so every aliased import silently
* collapses onto the importing module itself, and mixins, superclasses and
* re-exports lose their real location.
*
* This plugin normalizes aliased specifiers back to file paths, producing a
* manifest identical to the one the equivalent relative imports would.
*/
function resolveSubpathImportsPlugin() {
return {
name: 'IGC - RESOLVE SUBPATH IMPORTS',
/**
* Runs after `CORE - IMPORTS` has attached the module's imports to the
* context, and before any class declaration in it is analyzed. Rewriting to
* a root-relative path makes the analyzer's `new URL()` call resolve to the
* real module.
*/
analyzePhase({ ts, node, context }) {
if (node.kind !== ts.SyntaxKind.SourceFile) {
return;
}
for (const entry of context.imports ?? []) {
const alias = aliasOf(entry.importPath);
if (alias) {
entry.importPath = `/${toSourcePath(entry.importPath, alias)}`;
}
}
},
/**
* Re-exports keep the specifier verbatim rather than resolving it, so they
* are rewritten on the finished module - relative to the module itself, the
* form the analyzer emits for a relative import.
*/
moduleLinkPhase({ moduleDoc }) {
const relativize = (specifier, alias) => {
const relative = path.posix.relative(
path.posix.dirname(moduleDoc.path),
toSourcePath(specifier, alias)
);
return relative.startsWith('.') ? relative : `./${relative}`;
};
const visit = (value) => {
if (Array.isArray(value)) {
for (const item of value) {
visit(item);
}
return;
}
if (!value || typeof value !== 'object') {
return;
}
for (const [key, entry] of Object.entries(value)) {
if (key === 'module' && typeof entry === 'string') {
const alias = aliasOf(entry);
if (alias) {
value[key] = relativize(entry, alias);
}
} else {
visit(entry);
}
}
};
visit(moduleDoc);
},
};
}
/**
* Fields wrapped by the coerced-property decorator must keep an explicit
* `= undefined` initializer, which the analyzer records as
* `default: "undefined"` - noise that downstream consumers (the story generator
* among them) render as the literal string or coerce to `NaN`. These members
* shipped without a default as accessor pairs, so drop the entry.
*/
function stripUndefinedDefaultsPlugin() {
return {
name: 'IGC - STRIP UNDEFINED DEFAULTS',
moduleLinkPhase({ moduleDoc }) {
for (const declaration of moduleDoc.declarations ?? []) {
for (const member of declaration.members ?? []) {
if (member.default === 'undefined') {
delete member.default;
}
}
}
},
};
}
export default {
globs: ['src/**/*.ts'],
exclude: ['src/**/*.spec.ts', 'src/**/*.css.ts', 'src/**/themes/**'],
packagejson: true,
outdir: './',
litelement: true,
overrideModuleCreation: ({ ts, globs }) => {
const program = getTsProgram(ts, globs, 'tsconfig.json');
return program
.getSourceFiles()
.filter((sf) => globs.find((glob) => sf.fileName.includes(glob)));
},
plugins: [
resolveSubpathImportsPlugin(),
expandTypesPlugin({ hideLogs: true }),
stripUndefinedDefaultsPlugin(),
],
};