Skip to content

Commit

Permalink
Merge pull request #12 from kauderk/package-genereated-api
Browse files Browse the repository at this point in the history
adding support for standalone npm package
  • Loading branch information
Refzlund authored Apr 11, 2023
2 parents fcb4a3d + bc215db commit 54f91d2
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 56 deletions.
2 changes: 1 addition & 1 deletion install.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import C from './C.js'

try {
C.log(34)('Installing node modules...')()
execSync('pnpm remove -D sveltekit-zero-api', { stdio: 'inherit' })
// execSync('pnpm remove -D sveltekit-zero-api', { stdio: 'inherit' })
execSync('pnpm i', { stdio: 'inherit' })
} catch (error) {
console.error(C(31)('... An error occurred:')(), error)
Expand Down
2 changes: 1 addition & 1 deletion pack.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ let fileData = ''
try {
C.log(34)('Packaging')(0)
execSync('pnpm i --ignore-scripts')
execSync('pnpm remove sveltekit-zero-api')
// execSync('pnpm remove sveltekit-zero-api')
fileData = fs.readFileSync('svelte.config.js', 'utf-8')
fs.writeFileSync('svelte.config.js', fileData.replace(/(?<=Removed when packaging)[\s\S]*(?=\/\/ --)/, ''), 'utf-8')
execSync('set NODE_ENV=package')
Expand Down
85 changes: 46 additions & 39 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 43 additions & 13 deletions src/lib/api-types/api-updater.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
import { pathToImportPath, toValidVariable } from '../utils/string.js'
import fs from 'fs'
import { resolve } from 'path'
import { resolve, relative } from 'path'
import { debugging } from '$lib/internal.js'
import type { ZeroAPIPluginConfig } from '$lib/vitePlugin.js'

const cwd = process.cwd()

function deleteNestedEmptyObjects(obj: any) {
// modify by reference
Object.keys(obj).forEach(function(key) {
if (typeof obj[key] === 'object') {
deleteNestedEmptyObjects(obj[key])
if (Object.keys(obj[key]).length === 0) {
delete obj[key]
}
}
})
}
function parsePath(resolutionPath: string, path: string) {
const _relative = relative(resolutionPath, path).replace('..', '.')
const importName = pathToImportPath(_relative)
const alias = toValidVariable(importName)
return { importName, alias }
}

/** Is run when file changes has been detected */
export function apiUpdater(
config: ZeroAPIPluginConfig,
Expand All @@ -16,30 +34,39 @@ export function apiUpdater(
let apiTypes: Directory = {}
let importStatements = ''
debugging && console.time(`[DEBUG] Updated generated types at ${routesDirectory} ...`)
const { tempOutput, outputDir = 'src' } = config
const resolution = tempOutput ?
resolve(cwd, tempOutput) : resolve(cwd, '.svelte-kit', 'types', outputDir, 'sveltekit-zero-api.d.ts')

function recursiveLoad(dir: string, directory: Directory) {
const files = fs.readdirSync(dir)

// ex. src/routes/(app)/api/somedir/index.ts
for (const fileName of files) {
for (let fileName of files) {
const path = resolve(dir, fileName)
const metadata = fs.statSync(path)

if (metadata.isDirectory()) {
if (!directory[fileName])
if (!directory[fileName]){
if(fileName.match(/\[(.*?)\]/)){
// this should be a promise, it should resolve when dirText is parsed...
const { alias } = parsePath(resolution, path + '\\+server.ts') // comply with pathToImportPath
fileName = fileName.replace(/\[(.*?)\]/, `[$1]${alias}`)
}
directory[fileName] = {}
}
recursiveLoad(path, directory[fileName] as Directory)
continue
}

if (!/\+server.(ts|js)/gm.test(fileName))
continue

const importName = pathToImportPath(path)
const name = toValidVariable(importName)
importStatements += `import * as ${name} from "${importName}";\n`
const { alias, importName } = parsePath(resolution, path)
importStatements += `import * as ${alias} from "${importName}"\n`

const key = fileName.replace(/\.(ts|js)$/g, '')
directory[key] = `Z<typeof ${name}>`
directory[key] = `Z<typeof ${alias}>`
}
}
recursiveLoad(routesDirectory, apiTypes)
Expand All @@ -57,6 +84,8 @@ export function apiUpdater(
}
return obj
}

deleteNestedEmptyObjects(apiTypes)
apiTypes = fixKeys(apiTypes)

let dirText = JSON.stringify(apiTypes, null, 2)
Expand All @@ -69,13 +98,13 @@ export function apiUpdater(

// Transform slugs e.g. "[slug]": into functions slug$: (slug: S) =>
// TODO: Allow ex. [slug].[second] to become slug$second$: (slug: S, second: S) =>
.replaceAll(/\"\[(.*?)\]\"\:/g, '$1$: ($1: S) =>')
.replaceAll(/\"\[(.*?)\](.*?)\"\:/g, (match, p1, p2) => {
// FIXME: check for other cases
const alias = p2 || '{}';
return `${p1}$: (${p1}: Slug<typeof ${alias}>) =>`;
})
.replaceAll(/=\w+(?=:|\$)/g, '')

const { tempOutput, outputDir = 'src' } = config
const resolution = tempOutput ?
resolve(cwd, tempOutput) : resolve(cwd, '.svelte-kit', 'types', outputDir, 'sveltekit-zero-api.d.ts')

const folder = resolve(resolution, '..')
if (!fs.existsSync(folder))
fs.mkdirSync(folder, { recursive: true })
Expand All @@ -99,5 +128,6 @@ const file = (dirText: string, importCode: string) =>
import type { Z } from 'sveltekit-zero-api/types/zeroapi'
${importCode}
type S = string | number
type Slug<Module> = Module extends { Slug: infer S } ? S : string | number
export type GeneratedAPI = ${dirText}`
7 changes: 5 additions & 2 deletions src/lib/api/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function handler(options: IOptions, api: APIContent) {
if (stringifyQueryObjects && 'query' in api)
stringifyQuery(api)

const url = options.config.baseUrl || '' + options.path + ('query' in api ? '?' + new URLSearchParams(api.query).toString() : '')
const url = (options.config.baseUrl || '') + options.path + ('query' in api ? '?' + new URLSearchParams(api.query).toString() : '')
const baseData = options.config.baseData || {}

const isForm = Object.prototype.toString.call(api.body) === '[object FormData]'
Expand Down Expand Up @@ -89,7 +89,10 @@ export default function handler(options: IOptions, api: APIContent) {

const requestInit: RequestInit = { ...baseData, ...api, headers: { ...(baseData['headers'] || {}), ...(api['headers'] || {}) } }
if (api.body === undefined) delete requestInit['body']
const response = fetch(url, requestInit)
// avoid making the "preflight http request", which will make it twice as fast
const collapsedRequestInit = api.method === 'GET' ? undefined : requestInit

const response = fetch(url, collapsedRequestInit);
response.then(async (res) => {
const json = (res.headers.get('content-type')||'').includes('application/json') && await res[options.config.format || 'json']()
// TODO: Handle other responses than just JSON
Expand Down

0 comments on commit 54f91d2

Please sign in to comment.