Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding support for standalone npm package #12

Merged
merged 7 commits into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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