Skip to content

Commit

Permalink
refactor: create build for deno.land (#100)
Browse files Browse the repository at this point in the history
  • Loading branch information
stainless-bot authored and RobertCraigie committed Aug 1, 2023
1 parent f8ce4ec commit 02af2ec
Show file tree
Hide file tree
Showing 33 changed files with 362 additions and 50 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ node_modules
yarn-error.log
codegen.log
dist
/deno
/*.tgz
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
CHANGELOG.md
/ecosystem-tests
/node_modules
/deno
49 changes: 29 additions & 20 deletions build
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,12 @@ set -exuo pipefail

node scripts/check-version.cjs

RUNNER="yarn"
if ! command -v yarn &> /dev/null; then
RUNNER="npx"
fi

$RUNNER tsc

# Build into dist and will publish the package from there,
# so that src/resources/foo.ts becomes <package root>/resources/foo.js
# This way importing from `"@tryfinch/finch-api/resources/foo"` works
# even with `"moduleResolution": "node"`

rm -rf dist
mkdir dist
rm -rf dist; mkdir dist
# Copy src to dist/src and build from dist/src into dist, so that
# the source map for index.js.map will refer to ./src/index.ts etc
cp -rp src README.md dist
Expand All @@ -28,10 +20,10 @@ done
node scripts/make-dist-package-json.cjs > dist/package.json

# build to .js/.mjs/.d.ts files
$RUNNER tsc-multi
npm exec tsc-multi
# copy over handwritten .js/.mjs/.d.ts files
cp src/_shims/*.{d.ts,js,mjs} dist/_shims
$RUNNER tsc-alias -p tsconfig.build.json
npm exec tsc-alias -- -p tsconfig.build.json
# we need to add exports = module.exports = Finch Node to index.js;
# No way to get that from index.ts because it would cause compile errors
# when building .mjs
Expand All @@ -42,19 +34,36 @@ node scripts/fix-index-exports.cjs
# the same export default statement)
cp dist/index.d.ts dist/index.d.mts

SED=(sed -i)
if [[ "$OSTYPE" == "darwin"* ]]; then SED=(sed -i ''); fi

# strip out lib="dom" and types="node" references; these are needed at build time,
# but would pollute the user's TS environment
REFERENCE_SUBS='s/^ *\/\/\/ *<reference *lib="dom".*//g;s/^ *\/\/\/ *<reference *types="node".*//g'
if [[ "$OSTYPE" == "darwin"* ]]; then
find dist -type f -exec sed -i '' "${REFERENCE_SUBS}" {} +
else
find dist -type f -exec sed -i "${REFERENCE_SUBS}" {} +
fi
find dist -type f -exec "${SED[@]}" "${REFERENCE_SUBS}" {} +

$RUNNER prettier --loglevel=warn --write .
npm exec prettier -- --loglevel=warn --write .

# make sure that nothing crashes when we require the output CJS or
# import the output ESM
cd dist
node -e 'require("@tryfinch/finch-api")'
node -e 'import("@tryfinch/finch-api")' --input-type=module
(cd dist && node -e 'require("@tryfinch/finch-api")')
(cd dist && node -e 'import("@tryfinch/finch-api")' --input-type=module)

if command -v deno &> /dev/null
then
rm -rf deno; mkdir deno
cp -rp src/* README.md deno
rm deno/_shims/*.{d.ts,js,mjs,node.ts}
for file in deno/_shims/*.deno.ts; do
mv -- "$file" "${file%.deno.ts}.ts"
done
for file in LICENSE CHANGELOG.md; do
if [ -e "${file}" ]; then cp "${file}" deno; fi
done
npm exec ts-node -- scripts/denoify.ts
deno fmt deno


# make sure that nothing crashes when we load the Deno module
(cd deno && deno run index.ts)
fi
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
"jest": "^29.4.0",
"prettier": "rattrayalex/prettier#postfix-ternaries",
"ts-jest": "^29.1.0",
"ts-morph": "^19.0.0",
"ts-node": "^10.5.0",
"tsc-alias": "^1.8.6",
"tsc-multi": "^1.1.0",
Expand Down
190 changes: 190 additions & 0 deletions scripts/denoify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import path from 'path';
import * as tm from 'ts-morph';
import { name as pkgName } from '../package.json';

const rootDir = path.resolve(__dirname, '..');
const denoDir = path.join(rootDir, 'deno');
const tsConfigFilePath = path.join(rootDir, 'tsconfig.deno.json');

function denoify() {
const project = new tm.Project({ tsConfigFilePath });

for (const file of project.getSourceFiles()) {
if (!file.getFilePath().startsWith(denoDir + '/')) continue;
for (const decl of [...file.getImportDeclarations(), ...file.getExportDeclarations()]) {
const moduleSpecifier = decl.getModuleSpecifier();
if (!moduleSpecifier) continue;
let specifier = moduleSpecifier.getLiteralValue().replace(/^node:/, '');
if (!specifier) continue;

if (nodeStdModules.has(specifier)) {
// convert node builtins to deno.land/std
specifier = `https://deno.land/std@0.177.0/node/${specifier}.ts`;
} else if (specifier.startsWith(pkgName + '/')) {
// convert self-referencing module specifiers to relative paths
specifier = file.getRelativePathAsModuleSpecifierTo(denoDir + specifier.substring(pkgName.length));
} else if (!decl.isModuleSpecifierRelative()) {
continue;
}

if (decl.isModuleSpecifierRelative()) {
// there may be CJS directory module specifiers that implicitly resolve
// to /index.ts. Add an explicit /index.ts to the end
const sourceFile = decl.getModuleSpecifierSourceFile();
if (
sourceFile &&
/index\.[cm]?[jt]sx?$/.test(sourceFile.getFilePath()) &&
!/index(\.[cm]?[jt]sx?)?$/.test(specifier)
) {
specifier += '/' + path.basename(sourceFile.getFilePath());
}
}
// add explicit .ts file extensions to relative module specifiers
specifier = specifier.replace(/(\.[^./]*)?$/, '.ts');
moduleSpecifier.replaceWithText(JSON.stringify(specifier));
}

let addedBuffer = false,
addedProcess = false;
file.forEachDescendant((node) => {
switch (node.getKind()) {
case tm.ts.SyntaxKind.ExportDeclaration: {
const decl: tm.ExportDeclaration = node as any;
if (decl.isTypeOnly()) return;
for (const named of decl.getNamedExports()) {
// Convert `export { Foo } from './foo.ts'`
// to `export { type Foo } from './foo.ts'`
// if `./foo.ts` only exports types for `Foo`
if (!named.isTypeOnly() && !hasValueDeclarations(named)) {
named.replaceWithText(`type ${named.getText()}`);
}
}
break;
}
case tm.ts.SyntaxKind.ImportEqualsDeclaration: {
const decl: tm.ImportEqualsDeclaration = node as any;
if (decl.isTypeOnly()) return;

const ref = decl.getModuleReference();
if (ref.getText().includes('SinglePageResponse')) {
debugger;
}
if (!hasValueDeclarations(ref)) {
const params = ref.getType().getTypeArguments();
if (params.length) {
const paramsStr = params.map((p: tm.TypeParameter) => p.getText()).join(', ');
const bindingsStr = params
.map((p: tm.TypeParameter) => p.getSymbol()?.getName() || p.getText())
.join(', ');
decl.replaceWithText(
`export type ${decl.getName()}<${paramsStr}> = ${ref.getText()}<${bindingsStr}>`,
);
} else {
decl.replaceWithText(`export type ${decl.getName()} = ${ref.getText()}`);
}
}
break;
}
case tm.ts.SyntaxKind.Identifier: {
const id = node as tm.Identifier;
if (!addedBuffer && id.getText() === 'Buffer') {
addedBuffer = true;
file?.addVariableStatement({
declarations: [
{
name: 'Buffer',
type: 'any',
},
],
hasDeclareKeyword: true,
});
file?.addTypeAlias({
name: 'Buffer',
type: 'any',
});
}
if (!addedProcess && id.getText() === 'process') {
addedProcess = true;
file?.addVariableStatement({
declarations: [
{
name: 'process',
type: 'any',
},
],
hasDeclareKeyword: true,
});
}
}
}
});
}

project.save();
}

const nodeStdModules = new Set([
'assert',
'assertion_error',
'async_hooks',
'buffer',
'child_process',
'cluster',
'console',
'constants',
'crypto',
'dgram',
'diagnostics_channel',
'dns',
'domain',
'events',
'fs',
'global',
'http',
'http2',
'https',
'inspector',
'module_all',
'module_esm',
'module',
'net',
'os',
'path',
'perf_hooks',
'process',
'punycode',
'querystring',
'readline',
'repl',
'stream',
'string_decoder',
'sys',
'timers',
'tls',
'tty',
'upstream_modules',
'url',
'util',
'v8',
'vm',
'wasi',
'worker_threads',
'zlib',
]);

const typeDeclarationKinds = new Set([
tm.ts.SyntaxKind.InterfaceDeclaration,
tm.ts.SyntaxKind.ModuleDeclaration,
tm.ts.SyntaxKind.TypeAliasDeclaration,
]);

function hasValueDeclarations(nodes?: tm.Node): boolean;
function hasValueDeclarations(nodes?: tm.Node[]): boolean;
function hasValueDeclarations(nodes?: tm.Node | tm.Node[]): boolean {
if (nodes && !Array.isArray(nodes)) {
return hasValueDeclarations(nodes.getType().getSymbol()?.getDeclarations());
}
return nodes ? nodes.some((n) => !typeDeclarationKinds.has(n.getKind())) : false;
}

denoify();
23 changes: 23 additions & 0 deletions src/_shims/fetch.deno.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const _fetch = fetch;
const _Request = Request;
type _RequestInfo = RequestInfo;
type _RequestInit = RequestInit;
const _Response = Response;
type _ResponseInit = ResponseInit;
type _BodyInit = BodyInit;
const _Headers = Headers;
type _HeadersInit = HeadersInit;

export const isPolyfilled = false;

export {
_fetch as fetch,
_Request as Request,
type _RequestInfo as RequestInfo,
type _RequestInit as RequestInit,
_Response as Response,
type _ResponseInit as ResponseInit,
type _BodyInit as BodyInit,
_Headers as Headers,
type _HeadersInit as HeadersInit,
};
16 changes: 16 additions & 0 deletions src/_shims/formdata.deno.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
type _BlobPropertyBag = BlobPropertyBag;
type _FilePropertyBag = FilePropertyBag;

const _FormData = FormData;
const _File = File;
const _Blob = Blob;

export const isPolyfilled = false;

export {
_FormData as FormData,
_File as File,
_Blob as Blob,
type _BlobPropertyBag as BlobPropertyBag,
type _FilePropertyBag as FilePropertyBag,
};
4 changes: 2 additions & 2 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -737,8 +737,8 @@ const isAbsoluteURL = (url: string): boolean => {

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

const validatePositiveInteger = (name: string, n: number) => {
if (!Number.isInteger(n)) {
const validatePositiveInteger = (name: string, n: unknown): number => {
if (typeof n !== 'number' || !Number.isInteger(n)) {
throw new Error(`${name} must be an integer`);
}
if (n < 0) {
Expand Down
4 changes: 2 additions & 2 deletions src/pagination.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// File generated from our OpenAPI spec by Stainless.

import { AbstractPage, APIResponse, APIClient, FinalRequestOptions, PageInfo } from './core';
import * as HRIS from './resources/hris';
import * as ATS from './resources/ats';
import * as HRIS from './resources/hris/index';
import * as ATS from './resources/ats/index';

export type SinglePageResponse<Item> = Item[];

Expand Down
2 changes: 1 addition & 1 deletion src/resources/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import * as Core from '@tryfinch/finch-api/core';
import { APIResource } from '@tryfinch/finch-api/resource';
import * as API from './';
import * as API from './index';

export class Account extends APIResource {
/**
Expand Down
2 changes: 1 addition & 1 deletion src/resources/ats/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as Core from '@tryfinch/finch-api/core';
import { APIResource } from '@tryfinch/finch-api/resource';
import { isRequestOptions } from '@tryfinch/finch-api/core';
import * as Stages from '@tryfinch/finch-api/resources/ats/stages';
import * as API from './';
import * as API from './index';
import { ApplicationsPage, ApplicationsPageParams } from '@tryfinch/finch-api/pagination';

export class Applications extends APIResource {
Expand Down
2 changes: 1 addition & 1 deletion src/resources/ats/ats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Applications } from './applications';
import { Stages } from './stages';
import { Jobs } from './jobs';
import { Offers } from './offers';
import * as API from './';
import * as API from './index';

export class ATS extends APIResource {
candidates: Candidates = new Candidates(this.client);
Expand Down
2 changes: 1 addition & 1 deletion src/resources/ats/candidates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import * as Core from '@tryfinch/finch-api/core';
import { APIResource } from '@tryfinch/finch-api/resource';
import { isRequestOptions } from '@tryfinch/finch-api/core';
import * as API from './';
import * as API from './index';
import { CandidatesPage, CandidatesPageParams } from '@tryfinch/finch-api/pagination';

export class Candidates extends APIResource {
Expand Down
2 changes: 1 addition & 1 deletion src/resources/ats/jobs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import * as Core from '@tryfinch/finch-api/core';
import { APIResource } from '@tryfinch/finch-api/resource';
import { isRequestOptions } from '@tryfinch/finch-api/core';
import * as API from './';
import * as API from './index';
import { JobsPage, JobsPageParams } from '@tryfinch/finch-api/pagination';

export class Jobs extends APIResource {
Expand Down
Loading

0 comments on commit 02af2ec

Please sign in to comment.