From b8f5ac590f92b3b8ca1b18cecb1951f458fb9b65 Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Sat, 3 Aug 2024 16:33:59 +1000 Subject: [PATCH] TSDiffer add RAC (#6736) --- .../DocsTransformer.js | 22 ++++++- scripts/buildBranchAPI.js | 2 +- scripts/buildPublishedAPI.js | 2 +- scripts/compareAPIs.js | 59 ++++++++++++++----- 4 files changed, 66 insertions(+), 19 deletions(-) diff --git a/packages/dev/parcel-transformer-docs/DocsTransformer.js b/packages/dev/parcel-transformer-docs/DocsTransformer.js index aedc6e1f38e..588a27040f7 100644 --- a/packages/dev/parcel-transformer-docs/DocsTransformer.js +++ b/packages/dev/parcel-transformer-docs/DocsTransformer.js @@ -113,13 +113,15 @@ module.exports = new Transformer({ } if (path.isVariableDeclarator()) { if (!path.node.init) { + node.id = `${asset.filePath}:${path.node.id.name}`; + node.name = path.node.id.name; return Object.assign(node, {type: 'any'}); } let docs = getJSDocs(path.parentPath); processExport(path.get('init'), node); addDocs(node, docs); - if (node.type === 'interface') { + if (node.type === 'interface' || node.type === 'component') { node.id = `${asset.filePath}:${path.node.id.name}`; node.name = path.node.id.name; } @@ -342,6 +344,18 @@ module.exports = new Transformer({ }); } + if (path.isTSMappedType()) { + return Object.assign(node, { + type: 'mapped', + readonly: path.node.readonly, + typeParameter: { + ...processExport(path.get('typeParameter')), + isMappedType: true + }, + typeAnnotation: processExport(path.get('typeAnnotation')) + }); + } + if (path.isTSInterfaceDeclaration()) { let properties = {}; for (let propertyPath of path.get('body.body')) { @@ -495,7 +509,11 @@ module.exports = new Transformer({ } if (path.isTSAnyKeyword()) { - return Object.assign(node, {type: 'any'}); + return Object.assign(node, { + id: path.node.id ? `${asset.filePath}:${path.node.id.name}` : null, + name: path.node.id ? path.node.id.name : null, + type: 'any' + }); } if (path.isTSNullKeyword()) { diff --git a/scripts/buildBranchAPI.js b/scripts/buildBranchAPI.js index 65f6794d53b..3c48ba662e9 100644 --- a/scripts/buildBranchAPI.js +++ b/scripts/buildBranchAPI.js @@ -152,7 +152,7 @@ async function build() { // Build the website console.log('building api files'); - await run('yarn', ['parcel', 'build', 'packages/@react-{spectrum,aria,stately}/*/', 'packages/@internationalized/{message,string,date,number}', '--target', 'apiCheck'], {cwd: dir, stdio: 'inherit'}); + await run('yarn', ['parcel', 'build', 'packages/react-aria-components', 'packages/@react-{spectrum,aria,stately}/*/', 'packages/@internationalized/{message,string,date,number}', '--target', 'apiCheck'], {cwd: dir, stdio: 'inherit'}); // Copy the build back into dist, and delete the temp dir. fs.copySync(path.join(dir, 'packages'), distDir, {dereference: true}); diff --git a/scripts/buildPublishedAPI.js b/scripts/buildPublishedAPI.js index 5eadf56e108..b4056a93851 100644 --- a/scripts/buildPublishedAPI.js +++ b/scripts/buildPublishedAPI.js @@ -204,7 +204,7 @@ async function build() { // Build the website console.log('building api files'); - await run('yarn', ['parcel', 'build', 'packages/@react-{spectrum,aria,stately}/*/', 'packages/@internationalized/{message,string,date,number}'], {cwd: dir, stdio: 'inherit'}); + await run('yarn', ['parcel', 'build', 'packages/react-aria-components', 'packages/@react-{spectrum,aria,stately}/*/', 'packages/@internationalized/{message,string,date,number}', '--target', 'apiCheck'], {cwd: dir, stdio: 'inherit'}); // Copy the build back into dist, and delete the temp dir. // dev/docs/node_modules has some react spectrum components, we don't want those, and i couldn't figure out how to not build them diff --git a/scripts/compareAPIs.js b/scripts/compareAPIs.js index db94f401d5d..8a8f1925236 100644 --- a/scripts/compareAPIs.js +++ b/scripts/compareAPIs.js @@ -55,7 +55,9 @@ async function compare() { // find all matching pairs based on what's been published for (let pubApi of publishedAPIs) { let pubApiPath = pubApi.split(path.sep); - let sharedPath = path.join(...pubApiPath.slice(pubApiPath.length - 4)); + let pkgJson = fs.readJsonSync(path.join('/', ...pubApiPath.slice(0, pubApiPath.length - 2), 'package.json')); + let name = pkgJson.name; + let sharedPath = path.join(name, 'dist', 'api.json'); let found = false; for (let branchApi of branchAPIs) { if (branchApi.includes(sharedPath)) { @@ -72,7 +74,9 @@ async function compare() { // don't care about private APIs, but we do care if we're about to publish a new one for (let branchApi of branchAPIs) { let branchApiPath = branchApi.split(path.sep); - let sharedPath = path.join(...branchApiPath.slice(branchApiPath.length - 4)); + let pkgJson = fs.readJsonSync(path.join('/', ...branchApiPath.slice(0, branchApiPath.length - 2), 'package.json')); + let name = pkgJson.name; + let sharedPath = path.join(name, 'dist', 'api.json'); let found = false; for (let pubApi of publishedAPIs) { if (pubApi.includes(sharedPath)) { @@ -114,10 +118,11 @@ async function compare() { if (diff.length > 0) { let affected = followInvertedDependencies(simplifiedName, invertedDeps); // combine export change messages + // remove extra newline we added between the name of the interface and the properties to make the diffs easier to read changes.push(` #### ${simplifiedName} ${changedByDeps.length > 0 ? `changed by: - - ${changedByDeps.join('\n - ')}\n\n` : ''}${diff.length > 0 ? diff : ''}${affected.length > 0 ? ` + - ${changedByDeps.join('\n - ')}\n\n` : ''}${diff.split('\n').filter(line => line !== ' ').join('\n')}${affected.length > 0 ? ` it changed: - ${affected.join('\n - ')} ` : ''} @@ -371,6 +376,9 @@ function processType(value) { } return name; } + if (value.type === 'mapped') { + return `${value.readonly === '-' ? '-readonly' : ''}${processType(value.typeParameter)}: ${processType(value.typeAnnotation)}`; + } // interface still needed if we have it at top level? if (value.type === 'object') { if (value.properties) { @@ -428,7 +436,7 @@ ${value.exact ? '\\}' : '}'}`; return `keyof ${processType(value.keyof)}`; } - console.log('unknown type', value); + return `UNKNOWN: ${value.type}`; } function rebuildInterfaces(json) { @@ -445,10 +453,12 @@ function rebuildInterfaces(json) { let item = json.exports[key]; if (item?.type == null) { // todo what to do here?? + exports[item.name] = 'UNTYPED'; return; } if (item.props?.type === 'identifier') { // todo what to do here?? + exports[item.name] = 'UNTYPED'; return; } if (item.type === 'component') { @@ -479,8 +489,11 @@ function rebuildInterfaces(json) { compInterface[name] = {optional, defaultVal, value}; } let name = item.name ?? key; - if (item.typeParameters.length > 0) { - name = name + `<${item.typeParameters.map(processType).sort().join(', ')}>`; + if (item.typeParameters?.length > 0) { + compInterface['typeParameters'] = `<${item.typeParameters.map(processType).sort().join(', ')}>`; + } + if (item.props?.extends?.length > 0) { + compInterface['extend'] = `extends ${item.props.extends.map(processType).sort().join(', ')}`; } exports[name] = compInterface; } else if (item.type === 'function') { @@ -497,10 +510,11 @@ function rebuildInterfaces(json) { }); let returnVal = processType(item.return); let name = item.name ?? key; - if (item.typeParameters.length > 0) { - name = name + `<${item.typeParameters.map(processType).sort().join(', ')}>`; + funcInterface['returnVal'] = returnVal; + if (item.typeParameters?.length > 0) { + funcInterface['typeParameters'] = `<${item.typeParameters.map(processType).sort().join(', ')}>`; } - exports[name] = {...funcInterface, returnVal}; + exports[name] = funcInterface; } else if (item.type === 'interface') { let funcInterface = {}; Object.entries(item.properties).sort((([keyA], [keyB]) => keyA > keyB ? 1 : -1)).forEach(([, property]) => { @@ -515,8 +529,8 @@ function rebuildInterfaces(json) { funcInterface[name] = {optional, defaultVal, value}; }); let name = item.name ?? key; - if (item.typeParameters.length > 0) { - name = name + `<${item.typeParameters.map(processType).sort().join(', ')}>`; + if (item.typeParameters?.length > 0) { + funcInterface['typeParameters'] = `<${item.typeParameters.map(processType).sort().join(', ')}>`; } exports[name] = funcInterface; } else if (item.type === 'link') { @@ -532,7 +546,8 @@ function rebuildInterfaces(json) { exports[name] = {isType, optional, defaultVal, value}; } } else { - console.log('unknown top level export', item); + exports[key] = 'UNTYPED'; + // console.log('unknown top level export', key, item); } }); return exports; @@ -544,10 +559,24 @@ function formatProp([name, prop]) { function formatInterfaces(interfaces, allInterfaces) { return allInterfaces.map(name => { - if (interfaces[name]) { - let output = `${name} {\n`; - output += interfaces[name].isType ? formatProp(name, interfaces[name]) : Object.entries(interfaces[name]).map(formatProp).join('\n'); + let interfaceO = interfaces[name]; + if (interfaceO && interfaceO !== 'UNTYPED') { + let output = `${name}`; + if (interfaceO.typeParameters) { + output += ` ${interfaceO.typeParameters}`; + delete interfaceO.typeParameters; + } + if (interfaceO.extend) { + output += ` ${interfaceO.extend}`; + delete interfaceO.extend; + } + // include extra newline so that the names of the interface are always compared + output += ' {\n\n'; + output += interfaces[name].isType ? formatProp(name, interfaceO) : Object.entries(interfaceO).map(formatProp).join('\n'); return `${output}\n}\n`; + } else if (interfaceO === 'UNTYPED') { + // include extra newline so that the names of the interface are always compared + return `${name} {\n\n UNTYPED\n}\n`; } else { return '\n'; }