-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathbuild-ssr-stubs.js
95 lines (87 loc) · 2.74 KB
/
build-ssr-stubs.js
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
import { writeFileSync } from 'fs';
import { GlobalRegistrator } from '@happy-dom/global-registrator';
import prettier from 'prettier';
GlobalRegistrator.register();
/**
* @param {unknown} value
* @returns {value is Function}
*/
const isClass = (value) => {
return value.toString().startsWith('class');
};
/** @param {Function} klass */
const getClassStaticProperties = (klass) => {
/** @type {Record<string, unknown>} */
const extractedProperties = {};
/** @param {unknown} proto */
const extract = (proto) => {
if (proto === null) return;
if (proto === HTMLElement || proto === HTMLElement.prototype) {
return;
}
Object.getOwnPropertyNames(proto)
.filter((name) => {
const isPublic = !name.startsWith('_');
const isOwn = !['arguments', 'prototype', 'caller', 'constructor', 'name', 'length'].includes(name);
const isDefined = isOwn && !!proto[name];
return isPublic && isOwn && isDefined;
})
.forEach((name) => {
extractedProperties[name] = proto[name];
});
extract(Object.getPrototypeOf(proto));
};
extract(klass);
return extractedProperties;
};
/** @param {function} klass */
const stubClass = (klass) => {
const properties = getClassStaticProperties(klass);
return `class {
${Object.entries(properties)
.map(([key, value]) => {
let valueString;
if (typeof value === 'function') {
valueString = '() => {}';
} else if (typeof value === 'string') {
valueString = `\`${value}\``;
} else if (Array.isArray(value)) {
valueString = JSON.stringify(value);
} else if (typeof value === 'object') {
valueString = JSON.stringify(value);
} else {
throw new Error(`Unexpected property type: ${typeof value}`);
}
return `static ${key} = ${valueString}`;
})
.join('\n')}
}`;
};
const realExports = await import('./index.js');
const stubbedExports = Object.fromEntries(
Object.entries(realExports).map(([key, value]) => {
let newValue;
if (isClass(value)) {
newValue = stubClass(value);
} else if (typeof value === 'function') {
newValue = '() => {}';
} else if (typeof value === 'string') {
newValue = `\`${value}\``;
} else {
throw new Error(`Unexpected export type: ${typeof value}`);
}
return [key, newValue];
})
);
const content = Object.entries(stubbedExports)
.map(([key, value]) => {
return `export const ${key} = ${value};`;
})
.join('\n');
const formatted = await prettier.resolveConfig('./').then(
/** @param {Record<string, unknown>} options */
(options) => {
return prettier.format(content, { ...options, parser: 'babel' });
}
);
writeFileSync('./index.ssr.js', formatted);