Skip to content

Commit

Permalink
refactor(lockfile): consolidate TS and JSON schema type definitions (#…
Browse files Browse the repository at this point in the history
…777)

* feat(api): creation of a new `list` command

* Update packages/api/src/commands/list.ts

Co-authored-by: Kanad Gupta <[email protected]>

* Update packages/api/src/commands/list.ts

Co-authored-by: Kanad Gupta <[email protected]>

* Update packages/api/src/commands/list.ts

Co-authored-by: Kanad Gupta <[email protected]>

* fix: pr feedback

* feat(api): addition of a new `uninstall` command

* feat(api): outputting `language` to the `list` command

* docs: cleanup

* refactor: don't use enum

* refactor: use json schema for types

* ci: add bin script for updating files

* chore: update schema.json

* fix: use prettier to format schema.json

* refactor: rename file, clean up package.json scripts

* fix: node 20 compat and `ts-node --esm` not working there

* fix: bad merge... git can be mad dumb sometimes

* Update packages/api/src/lockfileSchema.ts

Co-authored-by: Jon Ursenbach <[email protected]>

* chore: rename var

Co-Authored-By: Jon Ursenbach <[email protected]>

* refactor: use existing map

Co-Authored-By: Jon Ursenbach <[email protected]>

* chore: stricter type

Co-Authored-By: Jon Ursenbach <[email protected]>

---------

Co-authored-by: Jon Ursenbach <[email protected]>
Co-authored-by: Jon Ursenbach <[email protected]>
  • Loading branch information
3 people authored Oct 20, 2023
1 parent e44461a commit eb76200
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 104 deletions.
28 changes: 28 additions & 0 deletions packages/api/bin/buildVersionedFiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Updates packageInfo.ts and schema.json with the latest package info.
* This script is run every time a new release is tagged.
*/
import fs from 'node:fs';

// eslint-disable-next-line import/no-extraneous-dependencies, node/no-extraneous-import
import prettier from 'prettier';

import pkg from '../package.json' assert { type: 'json' };
import { lockfileSchema } from '../src/lockfileSchema.js';

async function run() {
// use prettier to format schema file
const prettierConfig = await prettier.resolveConfig(process.cwd());
const formattedSchema = await prettier.format(JSON.stringify(lockfileSchema), { parser: 'json', ...prettierConfig });
fs.writeFileSync('./schema.json', formattedSchema);

const packageInfo = `
// This file is automatically updated by the build script.
export const PACKAGE_NAME = '${pkg.name}';
export const PACKAGE_VERSION = '${pkg.version}';
`.trimStart();

fs.writeFileSync('./src/packageInfo.ts', packageInfo);
}

run();
5 changes: 3 additions & 2 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
"scripts": {
"attw": "attw --pack --format table-flipped",
"build": "tsc",
"build:versionedfiles": "NODE_OPTIONS=--no-warnings node --loader ts-node/esm bin/buildVersionedFiles.ts",
"debug:bin": "NODE_OPTIONS=--no-warnings node --loader ts-node/esm src/bin.ts",
"lint:types": "tsc --noEmit",
"prebuild": "rm -rf dist/ && npm run version",
"prebuild": "rm -rf dist/ && npm run build:versionedfiles",
"prepack": "npm run build",
"test": "vitest run --coverage",
"test:smoke": "vitest --config=vitest-smoketest.config.ts",
"version": "node -p \"'// This file is automatically updated by the build script.\\nexport const PACKAGE_NAME = \\'' + require('./package.json').name + '\\';\\nexport const PACKAGE_VERSION = \\'' + require('./package.json').version + '\\';'\" > src/packageInfo.ts; git add src/packageInfo.ts"
"version": "npm run build:versionedfiles && git add schema.json src/packageInfo.ts"
},
"repository": {
"type": "git",
Expand Down
15 changes: 4 additions & 11 deletions packages/api/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,8 @@
"required": ["apis"],
"additionalProperties": false,
"properties": {
"$schema": {
"type": "string"
},
"apis": {
"type": "array",
"description": "The list of installed APIs",
"items": {
"$ref": "#/definitions/api"
}
}
"$schema": { "type": "string" },
"apis": { "type": "array", "description": "The list of installed APIs", "items": { "$ref": "#/definitions/api" } }
},
"definitions": {
"api": {
Expand Down Expand Up @@ -70,7 +62,8 @@
"description": "The date that this SDK was last rebuilt or updated.",
"examples": ["2023-10-19T20:35:39.268Z"]
}
}
},
"additionalProperties": false
}
}
}
15 changes: 10 additions & 5 deletions packages/api/src/codegen/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ import type Oas from 'oas';

import TSGenerator from './languages/typescript/index.js';

export enum SupportedLanguages {
JS = 'js',
}
export const SupportedLanguages = {
JS: 'js',
} as const;

type ObjectValues<T> = T[keyof T];

export type SupportedLanguage = ObjectValues<typeof SupportedLanguages>;

export interface InstallerOptions {
/**
* Will initiate a dry run install process. Used for simulating installations within a unit test.
Expand All @@ -21,7 +26,7 @@ export interface InstallerOptions {
}

export function codegenFactory(
language: SupportedLanguages,
language: SupportedLanguage,
spec: Oas,
specPath: string,
identifier: string,
Expand All @@ -36,7 +41,7 @@ export function codegenFactory(
}

export function uninstallerFactory(
language: SupportedLanguages,
language: SupportedLanguage,
storage: Storage,
opts: InstallerOptions = {},
): Promise<void> {
Expand Down
8 changes: 5 additions & 3 deletions packages/api/src/commands/install.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { SupportedLanguage } from '../codegen/factory.js';

import { Command, Option } from 'commander';
import figures from 'figures';
import Oas from 'oas';
Expand All @@ -21,10 +23,10 @@ cmd
new Option('-l, --lang <language>', 'SDK language').default(SupportedLanguages.JS).choices([SupportedLanguages.JS]),
)
.addOption(new Option('-y, --yes', 'Automatically answer "yes" to any prompts printed'))
.action(async (uri: string, options: { identifier?: string; lang: string; yes?: boolean }) => {
let language: SupportedLanguages;
.action(async (uri: string, options: { identifier?: string; lang: SupportedLanguage; yes?: boolean }) => {
let language: SupportedLanguage;
if (options.lang) {
language = options.lang as SupportedLanguages;
language = options.lang;
} else {
({ value: language } = await promptTerminal({
type: 'select',
Expand Down
86 changes: 86 additions & 0 deletions packages/api/src/lockfileSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import type { FromSchema } from '@readme/api-core/lib';

import { SupportedLanguages } from './codegen/factory.js';

const lockfileApiSchema = {
type: 'object',
required: ['createdAt', 'identifier', 'installerVersion', 'integrity'],
properties: {
createdAt: {
type: 'string',
format: 'date-time',
description: 'The date that this SDK was installed.',
examples: ['2023-10-19T20:35:39.268Z'],
},
identifier: {
type: 'string',
description:
"A unique identifier of the API. This'll be used to do imports on `@api/<identifier>` and also where the SDK code will be located in `.api/apis/<identifier>`",
examples: ['petstore'],
},
installerVersion: {
type: 'string',
description: 'The version of `api` that was used to install this SDK.',
examples: ['7.0.0'],
},
integrity: {
type: 'string',
description:
'An integrity hash that will be used to determine on `npx api update` calls if the API has changed since the SDK was last generated.',
examples: ['sha512-otRF5TLMeDczSJlrmWLNDHLfmXg+C98oa/I/X2WWycwngh+a6WsbnjTbfwKGRU5DFbagOn2qX2SRvtBGOBRVGg=='],
},
language: {
type: 'string',
description: 'The language that this SDK was generated for.',
default: SupportedLanguages.JS,
enum: Object.values(SupportedLanguages),
},
private: {
type: 'boolean',
description: 'Was this SDK installed as a private, unpublished, package to the filesystem?',
},
source: {
type: 'string',
description: 'The original source that was used to generate the SDK with.',
examples: [
'https://raw.githubusercontent.com/readmeio/oas-examples/main/3.0/json/petstore-simple.json',
'./petstore.json',
'@developers/v2.0#nysezql0wwo236',
],
},
updatedAt: {
type: 'string',
format: 'date-time',
description: 'The date that this SDK was last rebuilt or updated.',
examples: ['2023-10-19T20:35:39.268Z'],
},
},
additionalProperties: false,
} as const;

export const lockfileSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
title: 'api storage lockfile',
description: 'See https://api.readme.dev/docs',
type: 'object',
required: ['apis'],
additionalProperties: false,
properties: {
$schema: {
type: 'string',
},
apis: {
type: 'array',
description: 'The list of installed APIs',
items: {
$ref: '#/definitions/api',
},
},
},
definitions: {
api: lockfileApiSchema,
},
} as const;

export type Lockfile = FromSchema<typeof lockfileSchema>;
export type LockfileAPI = FromSchema<typeof lockfileApiSchema>;
88 changes: 5 additions & 83 deletions packages/api/src/storage.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { SupportedLanguage } from './codegen/factory.js';
import type { Lockfile, LockfileAPI } from './lockfileSchema.js';
import type { OASDocument } from 'oas/rmoas.types';

import fs from 'node:fs';
Expand Down Expand Up @@ -31,7 +33,7 @@ export default class Storage {
/**
* The language that this SDK was generated for.
*/
private language!: SupportedLanguages;
private language!: SupportedLanguage;

/**
* The identifier that this was installed as.
Expand All @@ -40,7 +42,7 @@ export default class Storage {
*/
identifier!: string;

constructor(source: string, language?: SupportedLanguages, identifier?: string) {
constructor(source: string, language?: SupportedLanguage, identifier?: string) {
Storage.setStorageDir();

this.fetcher = new Fetcher(source);
Expand Down Expand Up @@ -168,7 +170,7 @@ export default class Storage {
return res === undefined ? false : res;
}

setLanguage(language?: SupportedLanguages) {
setLanguage(language?: SupportedLanguage) {
// `language` wasn't always present in the lockfile so if we don't have one we should default
// to JS.
if (!language) {
Expand Down Expand Up @@ -359,83 +361,3 @@ export default class Storage {
fs.writeFileSync(Storage.getLockfilePath(), JSON.stringify(Storage.lockfile, null, 2));
}
}

/**
* @see schema.json
*/
export interface Lockfile {
/** @since 7.0.0 */
$schema: string;

/**
* The list of installed APIs.
*/
apis: LockfileAPI[];
}

/**
* @see schema.json
*/
export interface LockfileAPI {
/**
* The date that this SDK was installed.
*
* @since 7.0.0
* @example 2023-10-19T20:35:39.268Z
*/
createdAt: string;

/**
* A unique identifier of the API. This'll be used to do imports on `@api/<identifier>` and also
* where the SDK code will be located in `.api/apis/<identifier>`.
*
* @example petstore
*/
identifier: string;

/**
* The version of `api` that was used to install this SDK.
*
* @example 5.0.0
*/
installerVersion: string;

/**
* An integrity hash that will be used to determine on `npx api update` calls if the API has
* changed since the SDK was last generated.
*
* @example sha512-ld+djZk8uRWmzXC+JYla1PTBScg0NjP/8x9vOOKRW+DuJ3NNMRjrpfbY7T77Jgnc87dZZsU49robbQfYe3ukug==
*/
integrity: string;

/**
* The language that this SDK was generated for.
*
* @since 7.0.0
*/
language: SupportedLanguages;

/**
* Was this SDK installed as a private, unpublished, package to the filesystem?
*
* @since 7.0.0
*/
private?: boolean;

/**
* The original source that was used to generate the SDK with.
*
* @example https://raw.githubusercontent.com/readmeio/oas-examples/main/3.0/json/petstore-simple.json
* @example ./petstore.json
* @example @developers/v2.0#nysezql0wwo236
*/
source: string;

/**
* The date that this SDK was last rebuilt or updated.
*
* @since 7.0.0
* @example 2023-10-19T20:35:39.268Z
*/
updatedAt?: string;
}

0 comments on commit eb76200

Please sign in to comment.