Skip to content

Commit

Permalink
🔨 improve(patch): add @types/node to create bud app
Browse files Browse the repository at this point in the history
  • Loading branch information
kellymears committed Jun 5, 2024
1 parent 63225e4 commit 8d16ed5
Show file tree
Hide file tree
Showing 28 changed files with 699 additions and 102 deletions.
3 changes: 2 additions & 1 deletion sources/@roots/bud-framework/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@
"module": "./lib/index.js",
"devDependencies": {
"@skypack/package-check": "0.2.2",
"@types/node": "20.12.8"
"@types/node": "20.12.8",
"vitest": "1.6.0"
},
"dependencies": {
"@roots/bud-support": "workspace:*",
Expand Down
4 changes: 3 additions & 1 deletion sources/@roots/bud-framework/src/bud/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,9 @@ export class Bud {
* Constructor
*/
public constructor(context?: Context) {
if (context) this.context = {...context}
if (!context) throw BudError.normalize(`context is required`)

this.context = {...context}

this.set(`implementation`, this.constructor as any)

Expand Down
22 changes: 10 additions & 12 deletions sources/@roots/bud-framework/src/extension/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type * as Model from '@roots/bud-framework/extension/types'

import {Bud} from '@roots/bud-framework'
import {bind} from '@roots/bud-support/decorators/bind'
import {BudError, ExtensionError} from '@roots/bud-support/errors'
import {ExtensionError} from '@roots/bud-support/errors'
import get from '@roots/bud-support/get'
import isFunction from '@roots/bud-support/isFunction'
import isObject from '@roots/bud-support/isObject'
Expand Down Expand Up @@ -78,10 +78,6 @@ export class Extension<
const label =
this.label ?? this.constructor?.name ?? `unknown_extension`

if (error instanceof BudError) {
throw error
}

throw ExtensionError.normalize(error, {
docs: new URL(`https://bud.js.org/docs/extensions`),
issue: new URL(
Expand Down Expand Up @@ -186,20 +182,20 @@ export class Extension<
if (isFunction(valueOrCallback)) {
const resolved = valueOrCallback(this.get(key))
set(this._options, key, resolved)
this.logger.info(`set`, key, `=>`, resolved)
this.logger.info(`Set option:`, key, `=>`, resolved)
return this
}

set(this._options, key, valueOrCallback)
this.logger.info(`set`, key, `=>`, valueOrCallback)
this.logger.info(`Set option:`, key, `=>`, valueOrCallback)
return this
}

public set = this.setOption

@bind
public setOptions(value: Partial<Model.InternalOptions<Options>>): this {
this.logger.info(`set options`, value)
this.logger.info(`Set options:`, value)
this._options = value
return this
}
Expand Down Expand Up @@ -237,18 +233,20 @@ export class Extension<
return false
}

if (isUndefined(this[key])) return
if (isUndefined(this[key])) return false

if (this.meta[key] === true) return
if (this.meta[key] === true) return false
this.meta[key] = true

if ([`buildAfter`, `buildBefore`].includes(key) && !this.isEnabled())
return
return false

this.logger.log(`Executing:`, key)

await this[key](this.app)
await this.app.resolvePromises()

return true
}
}

Expand All @@ -269,4 +267,4 @@ export type {
WithOptions,
} from '@roots/bud-framework/extension/types'

export {DynamicOption}
export {DynamicOption, isDynamicOption}
17 changes: 9 additions & 8 deletions sources/@roots/bud-framework/src/methods/setPublicPath/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,16 @@ export interface setPublicPath {
* @see {@link https://bud.js.org/docs/bud.setPublicPath}
*/
export const setPublicPath: setPublicPath = function (publicPath) {
this.hooks.on(`build.output.publicPath`, publicPath)
this.hooks.on(`build.output.publicPath`, normalizePath(publicPath))

// Normalize the publicPath in case the user did not end it with a slash.
this.hooks.on(`build.output.publicPath`, (value = `auto`) => {
if (value === `` || value === `auto`) return value
if (!isString(value)) return value
return this
}

return !value.endsWith(`/`) ? value.concat(`/`) : value
})
const normalizePath = (
value: ((publicPath: string) => string) | string = `auto`,
) => {
if (!isString(value)) return value
if (value === `` || value === `auto`) return value

return this
return !value.endsWith(`/`) ? value.concat(`/`) : value
}
62 changes: 42 additions & 20 deletions sources/@roots/bud-framework/test/bootstrap.test.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,46 @@
import {Bud, factory} from '@repo/test-kit'
import {bootstrap as subject} from '@roots/bud-framework/bootstrap'
import {beforeEach, describe, expect, it, vi} from 'vitest'
import {path} from '@repo/constants'
import {factory} from '@repo/test-kit'
import {describe, expect, it} from 'vitest'

describe(
`bootstrap`,
function () {
let bud: Bud
let bootstrap: typeof subject
import {bootstrap} from '../src/bootstrap/index.js'
import {Bud} from '../src/bud/index.js'

beforeEach(async () => {
vi.clearAllMocks()
bud = await factory()
bud.context.basedir = `/foo`
bud.path = vi.fn(() => `/test-return`)
bootstrap = subject.bind(bud)
})
describe(`bootstrap`, {retry: 2}, function () {
it(`is a function`, () => {
expect(bootstrap).toBeInstanceOf(Function)
})

it(`returns Bud`, async () => {
const value = await bootstrap(
await factory({
basedir: path(`tests/util/project`),
dry: true,
}),
)
expect(value.constructor.name).toEqual(`Bud`)
})

it(`is a function`, () => {
expect(bootstrap).toBeInstanceOf(Function)
it(`should call methods`, async () => {
const value = await bootstrap(
await factory({
basedir: path(`tests/util/project`),
dry: true,
}),
)
expect(value.context.dry).toBe(true)
expect(value.context.basedir).toBe(path(`tests/util/project`))
})

it(`child instances should reference bud.root.module`, async () => {
const root = await factory({
basedir: path(`tests/util/project`),
dry: true,
})
},
{retry: 2},
)
await root.make(`foo`)
const child = root.get(`foo`)

const value = await bootstrap(child)
expect(value.isRoot).toBe(false)
expect(value.module).toBe(root.module)
})
})
17 changes: 13 additions & 4 deletions sources/@roots/bud-framework/test/bud.test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import {Bud} from '@roots/bud-framework/bud'
import type {Context} from '@roots/bud-framework'

import {describe, expect, it} from 'vitest'

import {Bud} from '../src/bud/index.js'

describe(`Bud`, function () {
it(`is a class`, () => {
expect(Bud).toBeInstanceOf(Function)
})

it(`is a constructor`, () => {
expect(new Bud()).toBeInstanceOf(Bud)
expect(new Bud({} as Context)).toBeInstanceOf(Bud)
})

it(`throws when bootstrapped with no context`, async () => {
it(`throws when constructed with no context`, async () => {
try {
// @ts-ignore
expect(await new Bud().lifecycle()).toThrowError()
expect(new Bud()).toThrowError()
} catch (e) {
expect(e).toBeInstanceOf(Error)
}
})

it(`has an executeServiceCallbacks method`, async () => {
expect(new Bud({} as Context).executeServiceCallbacks).toBeInstanceOf(
Function,
)
})
})
96 changes: 85 additions & 11 deletions sources/@roots/bud-framework/test/env.test.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,92 @@
import {factory} from '@repo/test-kit'
import {Bud} from '@roots/bud'
import Env from '@roots/bud-framework/env'
import {type Bud} from '@repo/test-kit'
import {beforeEach, describe, expect, it} from 'vitest'

describe(`@roots/bud/services/env`, () => {
let bud: Bud
import Env from '../src/env/index.js'

beforeEach(async () => {
bud = (await factory()) as Bud
})

it(`is a container service`, async () => {
const bud = await factory()
describe(`@roots/bud-framework/env`, () => {
it(`should be a container service`, async () => {
const bud = {
context: {
env: {},
},
} as Bud
const instance = new Env(() => bud)
expect(instance.constructor.name).toBe(`Env`)
})

describe(`getPublicEnv`, () => {
it(`should return the public env variables`, async () => {
const bud = {
context: {
env: {
PRIVATE_TEST: `test`,
PUBLIC_TEST: `test`,
},
},
} as unknown as Bud
const instance = new Env(() => bud)
await instance.bootstrap(bud)
expect(instance.getPublicEnv()).toStrictEqual({
TEST: `test`,
})
})
})

describe(`getEnv`, () => {
it(`should return the env variables`, async () => {
const bud = {
context: {
env: {
PRIVATE_TEST: `test`,
PUBLIC_TEST: `test`,
},
},
} as unknown as Bud
const instance = new Env(() => bud)
await instance.bootstrap(bud)
expect(instance.all()).toStrictEqual({
PRIVATE_TEST: `test`,
PUBLIC_TEST: `test`,
})
})
})

describe(`initial values`, async () => {
it(`should coerce 'true' to true`, async () => {
const bud = {
context: {
env: {
TEST: `true`,
},
},
} as unknown as Bud
const instance = new Env(() => bud)
await instance.bootstrap(bud)
expect(instance.get(`TEST`)).toBe(true)
})

it(`should coerce 'false' to false`, async () => {
const bud = {
context: {
env: {
TEST: `false`,
},
},
} as unknown as Bud
const instance = new Env(() => bud)
await instance.bootstrap(bud)
expect(instance.get(`TEST`)).toBe(false)
})

it(`should be fine with no initial env`, async () => {
const bud = {
context: {
env: {},
},
} as unknown as Bud
const instance = new Env(() => bud)
await instance.bootstrap(bud)
expect(instance.repository).toStrictEqual({})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {dependsOn} from '../../../src/extension/decorators/dependsOn.js'
@dependsOn([`foo`])
class TestClass {}

describe(`dependsOn`, () => {
describe(`@roots/bud-framework/extension/decorators/dependsOn`, () => {
it(`should return a decorator`, () => {
// @ts-ignore
expect(dependsOn([`foo`])).toBeInstanceOf(Function)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {dependsOnOptional} from '../../../src/extension/decorators/dependsOnOpti
@dependsOnOptional([`foo`])
class TestClass {}

describe(`dependsOnOptional`, () => {
describe(`@roots/bud-framework/extension/decorators/dependsOnOptional`, () => {
it(`should return a decorator`, () => {
// @ts-ignore
expect(dependsOnOptional([`foo`])).toBeInstanceOf(Function)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class TestClass {
}
}

describe(`development`, () => {
describe(`@roots/bud-framework/extension/decorators/development`, () => {
it(`should return a decorator`, () => {
expect(development).toBeInstanceOf(Function)
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {beforeAll, describe, expect, it, vi} from 'vitest'

import {options} from '../../../src/extension/decorators/options.js'
import {type Bud, Extension} from '../../../src/index.js'

// @ts-ignore
@options({
foo: `bar`,
})
class TestExtension extends Extension {}

describe(`@roots/bud-framework/extension/decorators/options`, () => {
let bud: Bud

beforeAll(async () => {
bud = {
module: {
import: vi.fn(async (...args: any[]) => {}),
resolve: vi.fn(async (...args: any[]) => {}),
},
resolvePromises: vi.fn(async (...args: any[]) => {}),
} as unknown as Bud
})

it(`should return a decorator`, () => {
// @ts-ignore
expect(options({foo: `bar`})).toBeInstanceOf(Function)
})

it(`should add a _options property to the class`, () => {
// @ts-ignore
expect(new TestExtension(bud)._options).toEqual({foo: `bar`})
})

it(`should add a options property to the class`, () => {
// @ts-ignore
expect(new TestExtension(bud).options).toEqual({foo: `bar`})
})
})
Loading

0 comments on commit 8d16ed5

Please sign in to comment.