Skip to content

Commit

Permalink
feat: change detection and continuous endpoint regeneration
Browse files Browse the repository at this point in the history
  • Loading branch information
kyr0 committed Jan 12, 2024
1 parent 5365534 commit 5bde6eb
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 16 deletions.
2 changes: 1 addition & 1 deletion example/todo-list/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
"dependencies": {
"astro": "^4.1.2",
"@astrojs/node": "^7.0.4",
"@jsheaven/astro-client-generator": "1.1.1"
"@jsheaven/astro-client-generator": "../../"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface ApiResponse {

/** return (await fetch('/api/create-todo', { method: 'POST', ... })).json() */
export const createTodo = async (payload: ApiRequest, options: RequestOptions = {}): Promise<ApiResponse> => {
let requestUrl = 'http://localhost:4321/api/create-todo'
let requestUrl = 'https://Air-von-Aron.fritz.box--4321.local-credentialless.webcontainer.io/api/create-todo'
if (options && options.query) {
requestUrl += '?' + Object.keys(options.query)
.map((key) => key + '=' + options.query![key])
Expand Down
2 changes: 1 addition & 1 deletion example/todo-list/src/pages/api-client/get-todos-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface ApiResponse {

/** return (await fetch('/api/get-todos', { method: 'GET', ... })).json() */
export const getTodos = async (options: RequestOptions = {}): Promise<ApiResponse> => {
let requestUrl = 'http://localhost:4321/api/get-todos'
let requestUrl = 'https://Air-von-Aron.fritz.box--4321.local-credentialless.webcontainer.io/api/get-todos'
if (options && options.query) {
requestUrl += '?' + Object.keys(options.query)
.map((key) => key + '=' + options.query![key])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export interface ApiResponse {

/** return (await fetch('/api/remove-todo', { method: 'DELETE', ... })).json() */
export const removeTodo = async (payload: ApiRequest, options: RequestOptions = {}): Promise<ApiResponse> => {
let requestUrl = 'http://localhost:4321/api/remove-todo'
let requestUrl = 'https://Air-von-Aron.fritz.box--4321.local-credentialless.webcontainer.io/api/remove-todo'
if (options && options.query) {
requestUrl += '?' + Object.keys(options.query)
.map((key) => key + '=' + options.query![key])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface ApiResponse {

/** return (await fetch('/api/update-todo', { method: 'PATCH', ... })).json() */
export const updateTodo = async (payload: ApiRequest, options: RequestOptions = {}): Promise<ApiResponse> => {
let requestUrl = 'http://localhost:4321/api/update-todo'
let requestUrl = 'https://Air-von-Aron.fritz.box--4321.local-credentialless.webcontainer.io/api/update-todo'
if (options && options.query) {
requestUrl += '?' + Object.keys(options.query)
.map((key) => key + '=' + options.query![key])
Expand Down
2 changes: 1 addition & 1 deletion example/todo-list/src/pages/api/create-todo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const POST: APIRoute = async ({ request }) => {

return new Response(
JSON.stringify({
status: 'SUCCESS',
status: 'SUCCESS', // no!
todos,
} as ApiResponse),
{
Expand Down
5 changes: 2 additions & 3 deletions example/todo-list/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -475,11 +475,10 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"

"@jsheaven/astro-client-generator@1.1.1":
"@jsheaven/astro-client-generator@../../":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@jsheaven/astro-client-generator/-/astro-client-generator-1.1.1.tgz#30da9b48c00fbd1cc2a70b51bdaff1be78eecd1c"
integrity sha512-ChCYiOg/m2ZC0QEAxtzbPIEvMOShBiPqij7ERHJ1ED0J4jiHRj1/nLVouAI96zbXbf3fdB0d2WPKr5QH39uMZg==
dependencies:
chokidar "^3.5.3"
kleur "^4.1.5"
ts-morph "^21.0.1"
yargs-parser "^21.1.1"
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jsheaven/astro-client-generator",
"version": "1.1.1",
"version": "1.1.2",
"type": "module",
"publishConfig": {
"access": "public"
Expand All @@ -24,9 +24,10 @@
"type": "git"
},
"scripts": {
"presandbox:example:dev": "IS_SANDBOX=true yarn --cwd ./example/todo-list",
"presandbox:example:dev": "yarn build && IS_SANDBOX=true yarn --cwd ./example/todo-list",
"sandbox:example:dev": "IS_SANDBOX=true yarn --cwd ./example/todo-list build",
"postsandbox:example:dev": "IS_SANDBOX=true yarn --cwd ./example/todo-list dev",
"pretest": "yarn build",
"test": "NODE_OPTIONS='--experimental-vm-modules --enable-source-maps --no-warnings' jest ./test/*.test.ts",
"clean": "rm -rf ./dist && rm -rf ./coverage",
"prebuild": "yarn clean",
Expand Down Expand Up @@ -61,6 +62,7 @@
"typescript": "^4.9.5"
},
"dependencies": {
"chokidar": "^3.5.3",
"kleur": "^4.1.5",
"ts-morph": "^21.0.1",
"yargs-parser": "^21.1.1"
Expand Down
51 changes: 46 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import fastGlob from 'fast-glob'
import { resolve, parse, sep } from 'path'
import { readFileSync, mkdirSync, writeFileSync } from 'fs'
import { Project } from 'ts-morph'
import { watch } from "chokidar"

export interface ApiClientGeneratorOptions {
/** folder to the API directory on disk (source code), default: './src/pages/api' */
Expand Down Expand Up @@ -82,6 +83,37 @@ export const apiClientGenerator = (
return {
name: 'astro-client-generator',
hooks: {
'astro:config:setup': async () => {

const watcher = watch(apiGeneratorOptions.apiDir, {
ignoreInitial: true,
ignorePermissionErrors: true,
ignored: /(^|[\/\\])\../, // ignore dotfiles
})

watcher.on('change', (path) => {
console.log('🖊️ Endpoint changed: ', path)
generateClientApis(apiGeneratorOptions)
})

watcher.on('add', (path, stats) => {

// check ctime vs mtime to avoid double-triggering
if (stats.ctimeMs === stats.mtimeMs) {
return
}

// check ctime older than 100ms to avoid initial triggering
if (stats.ctimeMs + 100 > Date.now()) {
return
}
console.log('🖊️ Endpoint added: ', path)
generateClientApis(apiGeneratorOptions)
})

// initial build
generateClientApis(apiGeneratorOptions)
},
'astro:build:done': async () => {
generateClientApis(apiGeneratorOptions)
},
Expand Down Expand Up @@ -285,6 +317,8 @@ export const groupByApiRoute = (routes: Array<ApiRouteParseResult>): ApiRoutePar

/** discovers the API endpoints, parses and statically analyzes their code: finally calls the codegen */
export const generateClientApis = (apiGeneratorOptions: ApiClientGeneratorOptions) => {

console.log('⚙️ Generating endpoint clients...')
apiGeneratorOptions = validateConfig(apiGeneratorOptions)

const apiFolder = resolve(process.cwd(), apiGeneratorOptions.apiDir)
Expand Down Expand Up @@ -338,17 +372,16 @@ export const generateClientApis = (apiGeneratorOptions: ApiClientGeneratorOption
)
const parsed = parse(clientFilePath)
const clientFileDir = parsed.dir
const finalFilePath = `${clientFileDir}${sep}${parsed.name}-client.ts`
const finalFilePath = `${clientFileDir}${sep}${parsed.name}-client.ts`.toLowerCase()

const project = new Project({
tsConfigFilePath: apiGeneratorOptions.tsConfigPath,
})

mkdirSync(clientFileDir, { recursive: true })


// refactor
const sourceFile = project.createSourceFile(finalFilePath.toLowerCase(), clientCode, {
// refactor and write-out
const sourceFile = project.createSourceFile(finalFilePath, clientCode, {
overwrite: true,
})

Expand All @@ -358,12 +391,20 @@ export const generateClientApis = (apiGeneratorOptions: ApiClientGeneratorOption
sourceFile.fixUnusedIdentifiers();
} while (lastWidth !== sourceFile.getFullWidth());

sourceFile.fixMissingImports();
sourceFile.organizeImports();
sourceFile.formatText();
sourceFile.saveSync();

//writeFileSync(finalFilePath.toLowerCase(), clientCode, { encoding: 'utf-8' })
// final formatting
const code = readFileSync(finalFilePath, { encoding: 'utf-8' })
// remove all double-blank lines (non-trivial to do using ts-morph)
const formattedCode = code.replace(/^\n\n$/g, '\n')
// remove all blank lines before opening brackets
writeFileSync(finalFilePath, formattedCode, { encoding: 'utf-8' })
})

console.log('🚀 Finished building endpoint clients.')
}

export const produceClientApiRequestImplCode = (
Expand Down
3 changes: 3 additions & 0 deletions test/astroClientGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ describe('apiClientGenerator', () => {
expect(result).toEqual({
name: 'astro-client-generator',
hooks: {
'astro:config:setup': expect.any(Function),
'astro:build:done': expect.any(Function),
},
})
Expand All @@ -406,6 +407,7 @@ describe('apiClientGenerator', () => {
expect(result).toEqual({
name: 'astro-client-generator',
hooks: {
'astro:config:setup': expect.any(Function),
'astro:build:done': expect.any(Function),
},
})
Expand All @@ -425,6 +427,7 @@ describe('apiClientGenerator', () => {
expect(result).toEqual({
name: 'astro-client-generator',
hooks: {
'astro:config:setup': expect.any(Function),
'astro:build:done': expect.any(Function),
},
})
Expand Down

0 comments on commit 5bde6eb

Please sign in to comment.