forked from mrmckeb/typescript-plugin-css-modules
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreateDtsExports.ts
129 lines (108 loc) · 3.76 KB
/
createDtsExports.ts
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
import { SourceMapConsumer } from 'source-map-js';
import { CustomTemplate, Options } from '../options';
import { transformClasses } from './classTransforms';
import { CSSExportsWithSourceMap } from './getCssExports';
import { VALID_VARIABLE_REGEXP } from './validVarRegexp';
import { Logger } from './logger';
const isValidVariable = (classname: string) =>
VALID_VARIABLE_REGEXP.test(classname);
const flattenClassNames = (
previousValue: string[] = [],
currentValue: string[],
) => previousValue.concat(currentValue);
export const createDtsExports = ({
cssExports,
fileName,
logger,
options,
}: {
cssExports: CSSExportsWithSourceMap;
fileName: string;
logger: Logger;
options: Options;
}): string => {
const classes = cssExports.classes;
const possiblyUndefined = Boolean(options.noUncheckedIndexedAccess);
const classnameToProperty = (classname: string) =>
`'${classname}'${possiblyUndefined ? '?' : ''}: string;`;
const classnameToNamedExport = (classname: string) =>
`export let ${classname}: string${
possiblyUndefined ? ' | undefined' : ''
};`;
const processedClasses = Object.keys(classes)
.map(transformClasses(options.classnameTransform))
.reduce(flattenClassNames, []);
const filteredClasses = processedClasses
.filter(isValidVariable)
.map(classnameToNamedExport);
let dts = '';
if (options.goToDefinition && cssExports.sourceMap) {
// Create a new source map consumer.
const smc = new SourceMapConsumer(cssExports.sourceMap);
// Split original CSS file into lines.
const cssLines = cssExports.css?.split('\n') ?? [];
// Create new equal size array of empty strings.
const dtsLines = Array.from(Array(cssLines.length), () => '');
// Create a list of filtered classnames and hashed classnames.
const filteredClasses = Object.entries(cssExports.classes)
.map(([classname, originalClassname]) => [
// TODO: Improve this. It may return multiple valid classnames and we
// want to handle all of those.
transformClasses(options.classnameTransform)(classname)[0],
originalClassname,
])
.filter(([classname]) => isValidVariable(classname));
filteredClasses.forEach(([classname, originalClassname]) => {
let matchedLine;
let matchedColumn;
for (let i = 0; i < cssLines.length; i++) {
const match = new RegExp(
// NOTE: This excludes any match not starting with:
// - `.` for classnames,
// - `:` or ` ` for animation names,
// and any matches followed by valid CSS selector characters.
`[:.\\s]${originalClassname}(?![_a-zA-Z0-9-])`,
'g',
).exec(cssLines[i]);
if (match) {
matchedLine = i;
matchedColumn = match.index;
break;
}
}
const { line: lineNumber } = smc.originalPositionFor({
// Lines start at 1, not 0.
line: matchedLine ? matchedLine + 1 : 1,
column: matchedColumn ? matchedColumn : 0,
});
dtsLines[lineNumber ? lineNumber - 1 : 0] +=
classnameToNamedExport(classname);
});
dts = dtsLines.join('\n');
}
dts += `\
declare let _classes: {
${processedClasses.map(classnameToProperty).join('\n ')}${
options.allowUnknownClassnames ? '\n [key: string]: string;' : ''
}
};
export default _classes;
`;
if (
!options.goToDefinition &&
options.namedExports !== false &&
filteredClasses.length
) {
dts += filteredClasses.join('\n') + '\n';
}
if (options.customTemplate) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const customTemplate = require(options.customTemplate) as CustomTemplate;
return customTemplate(dts, {
classes,
fileName,
logger,
});
}
return dts;
};