Skip to content

Commit 0f3cdba

Browse files
committed
Refactoring & tests
1 parent 5bb9ba1 commit 0f3cdba

File tree

11 files changed

+235
-161
lines changed

11 files changed

+235
-161
lines changed
File renamed without changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// <reference types="./url.d.ts" />
2+
3+
declare namespace CoreJS {
4+
export interface CoreJSURLConstructor {
5+
parse(url: string | CoreJSURL, base?: string | CoreJSURL): CoreJSURL | null;
6+
}
7+
}

packages/core-js-types/src/56/proposals/pure/url-search-params.d.ts renamed to packages/core-js-types/src/56/web/pure/url-search-params.d.ts

File renamed without changes.
File renamed without changes.

scripts/build-types/index.mjs

Lines changed: 2 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { getModulesMetadata } from '../build-entries/get-dependencies.mjs';
55
import config from './config.mjs';
66
import { argv, path, fs } from 'zx';
77
import { expandModules, modulesToStage } from '../build-entries/helpers.mjs';
8+
import { preparePureTypes } from './pure.mjs';
89

910
const { copy, outputFile, pathExists, readdir, remove } = fs;
1011

@@ -84,25 +85,6 @@ async function buildType(entry, options) {
8485
}
8586
}
8687

87-
async function getVersions() {
88-
const versions = [];
89-
const entries = await readdir(config.srcDir, { withFileTypes: true });
90-
for (const entry of entries) {
91-
if (entry.isDirectory()) {
92-
versions.push(parseInt(entry.name, 10));
93-
}
94-
}
95-
return versions.sort();
96-
}
97-
98-
async function buildTypesDirForTSVersion(version) {
99-
const versions = await getVersions();
100-
for (const v of versions) {
101-
if (v > version) break;
102-
await copy(path.join(config.srcDir, v.toString()), path.join(config.buildDir, version.toString(), 'types'));
103-
}
104-
}
105-
10688
const ESModules = AllModules.filter(it => it.startsWith('es.'));
10789
const ESWithProposalsModules = AllModules.filter(it => it.startsWith('es'));
10890
const StableModules = AllModules.filter(it => it.match(/^(?:es|web)\./));
@@ -150,7 +132,7 @@ async function buildTypesForTSVersion(tsVersion) {
150132
tsVersion = tsVersion.toString().replace('.', '');
151133
const bundlePath = path.join(config.buildDir, tsVersion);
152134
if (await pathExists(bundlePath)) await remove(bundlePath);
153-
await buildTypesDirForTSVersion(tsVersion);
135+
await copy(path.join(config.srcDir, tsVersion), path.join(config.buildDir, tsVersion, 'types'));
154136
await fillCustomImportsForPure(bundlePath, path.join(bundlePath, 'types'));
155137
await preparePureTypes(bundlePath);
156138

@@ -174,147 +156,6 @@ async function buildTypesForTSVersion(tsVersion) {
174156
await prependImports(tsVersion);
175157
}
176158

177-
function extractDeclareGlobalSections(lines) {
178-
const sections = [];
179-
const outside = [];
180-
for (let i = 0; i < lines.length;) {
181-
if (/^\s*declare\s+global\s*\{/.test(lines[i])) {
182-
let depth = 1;
183-
const section = [];
184-
for (++i; i < lines.length && depth > 0; ++i) {
185-
depth += (lines[i].match(/\{/g) || []).length;
186-
depth -= (lines[i].match(/\}/g) || []).length;
187-
if (depth === 0 && /^\s*\}\s*$/.test(lines[i])) break;
188-
if (depth > 0) section.push(lines[i]);
189-
}
190-
++i;
191-
sections.push(section);
192-
} else {
193-
outside.push(lines[i++]);
194-
}
195-
}
196-
return { sections, outside };
197-
}
198-
199-
function processLines(lines, prefix) {
200-
const prefixed = [];
201-
let noExport = false;
202-
return lines
203-
.map(line => {
204-
const hasOptions = line.includes('@type-options');
205-
const optionsStr = hasOptions ? line.match(/@type-options\s+(?<options>[\s\w,-]+)/)?.groups?.options : '';
206-
const options = {
207-
noExtends: !hasOptions ? false : optionsStr.includes('no-extends'),
208-
noPrefix: !hasOptions ? false : optionsStr.includes('no-prefix'),
209-
noConstructor: !hasOptions ? false : optionsStr.includes('no-constructor'),
210-
exportBaseConstructor: !hasOptions ? false : optionsStr.includes('export-base-constructor'),
211-
noExport: !hasOptions ? false : optionsStr.includes('no-export'),
212-
noRedefine: !hasOptions ? false : optionsStr.includes('no-redefine'),
213-
};
214-
if (noExport && /^[^{]*\}/.test(line)) {
215-
noExport = false;
216-
return null;
217-
}
218-
if (noExport) return null;
219-
if (options.noExport) {
220-
if (/^[^{]*$/.test(line) || /\{[^}]?\}/.test(line)) return null;
221-
noExport = true;
222-
return null;
223-
}
224-
if (/^\s*(?:declare\s+)?interface\s+\w+\s*extends/.test(line) || options.noExtends && /^\s*(?:declare\s+)?interface\s+\w+\s*\{/.test(line)) {
225-
if (!options.noPrefix) {
226-
const m = line.match(/interface\s+(?<name>\w+)/);
227-
if (m && m.groups) {
228-
prefixed.push(m.groups.name);
229-
}
230-
}
231-
return line.replace(/^(?<indent>\s*)(?:declare\s+)?interface\s+(?<name>[\s\w,<=>]+)/, `$<indent>export interface ${ !options.noPrefix ? prefix : '' }$<name>`);
232-
}
233-
if (!options.noExtends && /^\s*(?:declare\s+)?interface\s+\w+/.test(line)) {
234-
const m = line.match(/^(?<indent>\s*)(?:declare\s+)?interface\s+(?<name>\w+)(?<extend><[^>]+>)?/);
235-
const iIndent = m?.groups?.indent ?? '';
236-
const iName = m?.groups?.name ?? '';
237-
const iExtend = m?.groups?.extend ?? '';
238-
if (!options.noPrefix && iName !== '') {
239-
prefixed.push(iName);
240-
}
241-
const genericsForExtends = iExtend.replace(/\sextends\s[^,>]+/g, '').replace(/\s?=\s?\w+/g, '');
242-
const entityName = `${ !options.noPrefix ? prefix : '' }${ iName }`;
243-
const isConstructor = iName.includes('Constructor');
244-
let constructorDeclaration;
245-
if (isConstructor) {
246-
constructorDeclaration = !options.noRedefine ? `${ iIndent }var ${ entityName.replace('Constructor', '') }: ${ entityName };\n` : '';
247-
} else {
248-
constructorDeclaration = !options.noRedefine ? `${ iIndent }var ${ entityName }: ${ options.exportBaseConstructor ? iName : entityName }${ options.noConstructor ? '' : 'Constructor' };\n` : '';
249-
}
250-
return `${ constructorDeclaration }${ iIndent }export interface ${ entityName }${ iExtend } extends ${ iName }${ genericsForExtends } {\n`;
251-
}
252-
if (/^\s*(?:declare\s+)?function/.test(line)) {
253-
return line.replace(/^(?<indent>\s*)(?:declare\s+)?function\s+(?<name>\w+)/, `$<indent>export function ${ !options.noPrefix ? prefix : '' }$<name>`);
254-
}
255-
if (/:\s*\w/.test(line)) {
256-
const sortedPrefixed = prefixed.sort((a, b) => b.length - a.length);
257-
sortedPrefixed.forEach(item => {
258-
const reg = new RegExp(`: ${ item }([,;<)])`, 'g');
259-
line = line.replace(reg, `: ${ prefix }${ item }$1`);
260-
});
261-
}
262-
if (/^\s*(?:declare)?\svar/.test(line)) {
263-
const m = line.match(/^(?<indent>\s*)(?:declare\s+)?var\s+(?<name>\w+):\s+(?<type>\w+)/);
264-
return `${ m?.groups?.indent ?? '' }var ${ !options.noPrefix ? prefix : '' }${ m?.groups?.name ?? '' }: ${ m?.groups?.type };\n`;
265-
}
266-
return line;
267-
})
268-
.filter(line => line !== null);
269-
}
270-
271-
function wrapDTSInNamespace(content, namespace = 'CoreJS') {
272-
const lines = content.split('\n');
273-
const preamble = [];
274-
let i = 0;
275-
for (; i < lines.length; i++) {
276-
const line = lines[i];
277-
if (/\/\/\/\s*<reference types/.test(line)) {
278-
const m = line.match(/\/\/\/\s*<reference types="(?<path>[^"]+)"/);
279-
const typePath = m?.groups?.path ?? '';
280-
preamble.push(line.replace(typePath, `../${ typePath }`));
281-
continue;
282-
}
283-
if (/^\s*import /.test(line)) {
284-
preamble.push(line.replace(/^\s*import\s.*from\s+["'].+["']/, (_, a, b, c) => `${ a }../${ b }${ c }`));
285-
} else if (/^\s*\/\//.test(line) || /^\s*$/.test(line)) {
286-
preamble.push(line);
287-
} else break;
288-
}
289-
const mainLines = lines.slice(i);
290-
const { sections, outside } = extractDeclareGlobalSections(mainLines);
291-
const nsBody = [...processLines(outside, namespace), ...sections.flatMap(s => processLines(s, namespace))]
292-
.reduce((res, line) => {
293-
if ((line && line.trim() !== '') || (res.at(-1) && res.at(-1).trim() !== '')) res.push(line);
294-
return res;
295-
}, []).map(line => line ? ` ${ line }` : '').join('\n');
296-
return `${ preamble.length ? `${ preamble.join('\n') }\n` : '' }declare namespace ${ namespace } {\n${ nsBody }}\n`;
297-
}
298-
299-
async function preparePureTypes(typesPath) {
300-
const entries = await readdir(typesPath, { withFileTypes: true });
301-
for (const entry of entries) {
302-
if (entry.name === 'pure') continue;
303-
if (entry.isDirectory()) {
304-
await preparePureTypes(path.join(typesPath, entry.name));
305-
} else {
306-
if (entry.name.includes('core-js-types.d.ts')) continue;
307-
const typePath = path.join(typesPath, entry.name);
308-
const resultFilePath = typePath.replace(entry.name, `pure/${ entry.name }`);
309-
if (await pathExists(resultFilePath)) continue;
310-
const content = await fs.readFile(typePath, 'utf8');
311-
if (content.includes('declare namespace')) continue;
312-
const result = wrapDTSInNamespace(content);
313-
await outputFile(resultFilePath, result);
314-
}
315-
}
316-
}
317-
318159
if (VERSION) {
319160
await buildTypesForTSVersion(VERSION);
320161
} else {

scripts/build-types/pure.mjs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { path, fs } from 'zx';
2+
3+
const { outputFile, pathExists, readdir } = fs;
4+
5+
function extractDeclareGlobalSections(lines) {
6+
const sections = [];
7+
const outside = [];
8+
for (let i = 0; i < lines.length;) {
9+
if (/^\s*declare\s+global\s*\{/.test(lines[i])) {
10+
let depth = 1;
11+
const section = [];
12+
for (++i; i < lines.length && depth > 0; ++i) {
13+
depth += (lines[i].match(/\{/g) || []).length;
14+
depth -= (lines[i].match(/\}/g) || []).length;
15+
if (depth === 0 && /^\s*\}\s*$/.test(lines[i])) break;
16+
if (depth > 0) section.push(lines[i]);
17+
}
18+
++i;
19+
sections.push(section);
20+
} else {
21+
outside.push(lines[i++]);
22+
}
23+
}
24+
return { sections, outside };
25+
}
26+
27+
function processLines(lines, prefix) {
28+
const prefixed = [];
29+
let noExport = false;
30+
return lines
31+
.map(line => {
32+
const hasOptions = line.includes('@type-options');
33+
const optionsStr = hasOptions ? line.match(/@type-options\s+(?<options>[\s\w,-]+)/)?.groups?.options : '';
34+
const options = {
35+
noExtends: !hasOptions ? false : optionsStr.includes('no-extends'),
36+
noPrefix: !hasOptions ? false : optionsStr.includes('no-prefix'),
37+
noConstructor: !hasOptions ? false : optionsStr.includes('no-constructor'),
38+
exportBaseConstructor: !hasOptions ? false : optionsStr.includes('export-base-constructor'),
39+
noExport: !hasOptions ? false : optionsStr.includes('no-export'),
40+
noRedefine: !hasOptions ? false : optionsStr.includes('no-redefine'),
41+
};
42+
if (noExport && /^[^{]*\}/.test(line)) {
43+
noExport = false;
44+
return null;
45+
}
46+
if (noExport) return null;
47+
if (options.noExport) {
48+
if (/^[^{]*$/.test(line) || /\{[^}]?\}/.test(line)) return null;
49+
noExport = true;
50+
return null;
51+
}
52+
if (line.includes('export {')) return null;
53+
if (/^\s*(?:declare\s+)?interface\s+\w+\s*extends/.test(line) || options.noExtends && /^\s*(?:declare\s+)?interface\s+\w+\s*\{/.test(line)) {
54+
if (!options.noPrefix) {
55+
const m = line.match(/interface\s+(?<name>\w+)/);
56+
if (m && m.groups) {
57+
prefixed.push(m.groups.name);
58+
}
59+
}
60+
return line.replace(/^(?<indent>\s*)(?:declare\s+)?interface\s+(?<name>[\s\w,<=>]+)/, `$<indent>export interface ${ !options.noPrefix ? prefix : '' }$<name>`);
61+
}
62+
if (!options.noExtends && /^\s*(?:declare\s+)?interface\s+\w+/.test(line)) {
63+
const m = line.match(/^(?<indent>\s*)(?:declare\s+)?interface\s+(?<name>\w+)(?<extend><[^>]+>)?/);
64+
const iIndent = m?.groups?.indent ?? '';
65+
const iName = m?.groups?.name ?? '';
66+
const iExtend = m?.groups?.extend ?? '';
67+
if (!options.noPrefix && iName !== '') {
68+
prefixed.push(iName);
69+
}
70+
const genericsForExtends = iExtend.replace(/\sextends\s[^,>]+/g, '').replace(/\s?=\s?\w+/g, '');
71+
const entityName = `${ !options.noPrefix ? prefix : '' }${ iName }`;
72+
const isConstructor = iName.includes('Constructor');
73+
let constructorDeclaration;
74+
if (isConstructor) {
75+
constructorDeclaration = !options.noRedefine ? `${ iIndent }var ${ entityName.replace('Constructor', '') }: ${ entityName };\n` : '';
76+
} else {
77+
constructorDeclaration = !options.noRedefine ? `${ iIndent }var ${ entityName }: ${ options.exportBaseConstructor ? iName : entityName }${ options.noConstructor ? '' : 'Constructor' };\n` : '';
78+
}
79+
return `${ constructorDeclaration }${ iIndent }export interface ${ entityName }${ iExtend } extends ${ iName }${ genericsForExtends } {\n`;
80+
}
81+
if (/^\s*(?:declare\s+)?function/.test(line)) {
82+
return line.replace(/^(?<indent>\s*)(?:declare\s+)?function\s+(?<name>\w+)/, `$<indent>export function ${ !options.noPrefix ? prefix : '' }$<name>`);
83+
}
84+
if (/:\s*\w/.test(line)) {
85+
const sortedPrefixed = prefixed.sort((a, b) => b.length - a.length);
86+
sortedPrefixed.forEach(item => {
87+
const reg = new RegExp(`: ${ item }([,;<)])`, 'g');
88+
line = line.replace(reg, `: ${ prefix }${ item }$1`);
89+
});
90+
}
91+
if (/^\s*(?:declare)?\svar/.test(line)) {
92+
const m = line.match(/^(?<indent>\s*)(?:declare\s+)?var\s+(?<name>\w+):\s+(?<type>\w+)/);
93+
return `${ m?.groups?.indent ?? '' }var ${ !options.noPrefix ? prefix : '' }${ m?.groups?.name ?? '' }: ${ m?.groups?.type };\n`;
94+
}
95+
return line;
96+
})
97+
.filter(line => line !== null);
98+
}
99+
100+
function wrapDTSInNamespace(content, namespace = 'CoreJS') {
101+
const lines = content.split('\n');
102+
const preamble = [];
103+
let i = 0;
104+
for (; i < lines.length; i++) {
105+
const line = lines[i];
106+
if (/\/\/\/\s*<reference types/.test(line)) {
107+
const m = line.match(/\/\/\/\s*<reference types="(?<path>[^"]+)"/);
108+
const typePath = m?.groups?.path ?? '';
109+
preamble.push(line.replace(typePath, `../${ typePath }`));
110+
continue;
111+
}
112+
if (/^\s*import /.test(line)) {
113+
preamble.push(line.replace(/^\s*import\s.*from\s+["'].+["']/, (_, a, b, c) => `${ a }../${ b }${ c }`));
114+
} else if (/^\s*\/\//.test(line) || /^\s*$/.test(line)) {
115+
preamble.push(line);
116+
} else break;
117+
}
118+
const mainLines = lines.slice(i);
119+
const { sections, outside } = extractDeclareGlobalSections(mainLines);
120+
const nsBody = [...processLines(outside, namespace), ...sections.flatMap(s => processLines(s, namespace))]
121+
.reduce((res, line) => {
122+
if ((line && line.trim() !== '') || (res.at(-1) && res.at(-1).trim() !== '')) res.push(line);
123+
return res;
124+
}, []).map(line => line ? ` ${ line }` : '').join('\n');
125+
return `${ preamble.length ? `${ preamble.join('\n') }\n` : '' }declare namespace ${ namespace } {\n${ nsBody }\n}\n`;
126+
}
127+
128+
export async function preparePureTypes(typesPath) {
129+
const entries = await readdir(typesPath, { withFileTypes: true });
130+
for (const entry of entries) {
131+
if (entry.name === 'pure') continue;
132+
if (entry.isDirectory()) {
133+
await preparePureTypes(path.join(typesPath, entry.name));
134+
} else {
135+
if (entry.name.includes('core-js-types.d.ts')) continue;
136+
const typePath = path.join(typesPath, entry.name);
137+
const resultFilePath = typePath.replace(entry.name, `pure/${ entry.name }`);
138+
if (await pathExists(resultFilePath)) continue;
139+
const content = await fs.readFile(typePath, 'utf8');
140+
if (content.includes('declare namespace')) continue;
141+
const result = wrapDTSInNamespace(content);
142+
await outputFile(resultFilePath, result);
143+
}
144+
}
145+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import $date from '@core-js/pure/full/date/index';
2+
3+
new $date();
4+
new $date('2020-01-01');
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import $canParse from '@core-js/pure/full/url/can-parse';
2+
3+
const u1: boolean = $canParse('https://example.com/path?name=value#hash');
4+
const u2: boolean = $canParse('/path', 'https://example.com');
5+
6+
// @ts-expect-error
7+
$canParse(null);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import $parse from '@core-js/pure/full/url/parse';
2+
3+
const u1 = $parse('https://example.com/path?name=value#hash');
4+
$parse('/path', 'https://example.com');
5+
6+
if (u1) {
7+
$parse(u1);
8+
9+
let str: string;
10+
str = u1.pathname;
11+
str = u1.hostname;
12+
str = u1.pathname;
13+
14+
str = u1.toJSON();
15+
str = u1.toString();
16+
}
17+
18+
// @ts-expect-error
19+
$parse(null);

0 commit comments

Comments
 (0)