Skip to content

Commit

Permalink
fix: bud.alias (#1283)
Browse files Browse the repository at this point in the history
* fix bud.alias
  • Loading branch information
kellymears committed Mar 22, 2022
1 parent 0d78839 commit b854f95
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 159 deletions.
16 changes: 0 additions & 16 deletions sources/@roots/bud-api/src/api/methods/alias/alias.interface.ts

This file was deleted.

38 changes: 0 additions & 38 deletions sources/@roots/bud-api/src/api/methods/alias/alias.method.ts

This file was deleted.

35 changes: 32 additions & 3 deletions sources/@roots/bud-api/src/api/methods/alias/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
export type {facade} from './alias.interface'
export {alias as method} from './alias.method'
export {alias} from './alias.method'
import type {Framework} from '@roots/bud-framework'
import type {Configuration} from 'webpack'

export interface facade {
(alias: Configuration['resolve']['alias']): Framework
}

export interface alias {
(alias: Configuration['resolve']['alias']): Promise<Framework>
}

export const alias: alias = async function (input) {
const app = this as Framework

input = Object.entries(input).reduce((a, [k, v]) => {
if (v.startsWith('@')) {
return {...a, [k]: app.path(v)}
}

if (!v.startsWith('/')) {
return {...a, [k]: app.path(v)}
}

return {...a, [k]: v}
}, {})

app.hooks.async('build.resolve.alias', async aliases => {
return {...(aliases ?? {}), ...(input ?? {})}
})

return app
}
2 changes: 1 addition & 1 deletion sources/@roots/bud-api/src/api/methods/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export {method as alias} from './alias'
export {alias} from './alias'
export {method as assets, method as copy} from './assets'
export {
config,
Expand Down
78 changes: 35 additions & 43 deletions sources/@roots/bud-framework/src/Framework/methods/path.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import {join, resolve, sep as slash} from 'node:path'
import {resolve, sep as slash} from 'node:path'

import {Framework} from '../..'

export interface path {
(base?: string, ...segments: Array<string>): string
}

/**
* Transform `@alias` path
*
Expand All @@ -15,56 +11,52 @@ export interface path {
*
* @public
*/
const transformShorthandBase = (app: Framework, base: string): string => {
/**
* If path contains multiple segments, explode into an array
* If path contains one segment, insert it into a blank array
*/
const [ident, ...parts] = base.includes(slash)
? base.split(slash)
: [base]
export interface parseAlias {
(app: Framework, base: `@${string}` & string): string
}

export const parseAlias: parseAlias = (app, base) => {
/* Normalize base path to an array of path segments */
let [ident, ...parts] = base.includes(slash) ? base.split(slash) : [base]

/* If there is no match for ident there is a problem */
!app.hooks.has(`location.${ident}`) &&
app.error(
`\`${ident}\` is not a registered path. It must be defined with bud.setPath`,
)

/**
* Replace
*/
const value = app.hooks.filter(`location.${ident}`)
/* Replace base path */
ident = app.hooks.filter(`location.${ident}`)

return join(value, ...(parts ?? []).filter(Boolean))
/* If segments were passed, resolve */
return parts.length ? resolve(ident, ...parts) : ident
}

export const path: path = function (
base?: string,
...segments: Array<string>
): string {
/**
* Transform `@alias` path
*
* @param app - Framework instance
* @param base - Path segment
* @returns string
*
* @public
*/
export interface path {
(base?: string, ...segments: Array<string>): string
}

export const path: path = function (base, ...segments) {
const app = this as Framework
/**
* If no base path was provided return the project directory
*/
if (!base) return app.context.projectDir

/**
* If base path starts with `/` return the joined path and segments (if any)
*/
if (base.startsWith('/'))
return segments.length ? join(base, ...segments) : base
/* Exit early with projectDir if no path was passed */
if (!base) return app.context.projectDir

/**
* Replace any `@alias` aliases with their corresponding entry
*/
const normalized = base.startsWith(`@`)
? transformShorthandBase(app, base)
: base
/* Parse `@` aliases. Should return an absolute path */
if (base.startsWith(`@`)) base = parseAlias(app, base as `@${string}`)

const absolutePath = base.startsWith(`/`)
? normalized
: resolve(app.context.projectDir, normalized)
/* Resolve any base path that isn't already absolute */
if (!base.startsWith(`/`)) base = resolve(app.context.projectDir, base)

return segments.length
? resolve(absolutePath, ...(segments ?? []).filter(Boolean))
: absolutePath
/* If segments were passed, resolve them against base */
return segments.length ? resolve(base, ...segments) : base
}
64 changes: 25 additions & 39 deletions sources/@roots/bud-framework/src/Framework/methods/setPath.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,11 @@
import {lodash} from '@roots/bud-support'

import * as Framework from '../..'
import {Framework, Locations} from '../..'

const {isString} = lodash

/**
* setPath function interface
*
* @internal
*/
export interface setPath {
<T extends `${keyof Framework.Locations & string}`>(
arg1: T | Record<T, string>,
arg2?: string,
): Framework.Framework
}

const transformShorthandBase = (
app: Framework.Framework,
base: string,
): string => {
const parts = base.includes('/') ? base.split('/') : [base]
parts[0] = app.hooks.filter(`location.${parts[0]}`)
return parts.join('/')
}

/**
* Set a {@link @roots/bud-framework#Location | Location} value
*
* @remarks
* The {@link Location.project} should be an absolute path.
* All other directories should be relative (src, dist, etc.)
* @see {@link Locations}
* Set a reference to a project path
*
* @example
* ```js
Expand All @@ -40,21 +14,33 @@ const transformShorthandBase = (
*
* @public
*/
export function setPath<
T extends `${Omit<'project', keyof Framework.Locations> & string}`,
>(arg1: T | Record<T, string>, arg2?: string): Framework.Framework {
const ctx = this as Framework.Framework
export interface setPath {
<T extends `${keyof Locations & `@${string}` & string}`>(
arg1: T | Record<T, string>,
arg2?: string,
): Framework
}

export const setPath: setPath = function (arg1, arg2) {
const app = this as Framework

const input = isString(arg1) ? {[arg1]: arg2} : arg1

Object.entries(input).map(([key, value]: [string, string]) => {
value = value.startsWith(`@`)
? transformShorthandBase(ctx, value)
: value
Object.entries(input).map(([key, value]) => {
!key.startsWith(`@`) &&
app.error(
`bud paths are required to be prefixed with \`@\`. Please convert \`${key}\` to \`@${key}\``,
)

const absolutePath = app.path(value)
!absolutePath.startsWith('/') &&
app.error(
`internal error: the final result of a bud.setPath transform was not absolute: ${key} => ${value} => ${absolutePath}`,
)

ctx.hooks.on(`location.${key}`, value)
ctx.info(`${key} set to ${value}`)
app.hooks.on(`location.${key}`, app.path(value))
app.info(`${key} set to ${value}`)
})

return ctx
return app
}
9 changes: 4 additions & 5 deletions sources/@roots/sage/src/sage.preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ const Sage: Sage = {
app.setPath({
'@src': 'resources',
'@dist': 'public',
'@app': 'app',
'@resources': '@src',
'@public': '@dist',
'@fonts': '@src/fonts',
Expand All @@ -64,10 +63,10 @@ const Sage: Sage = {
* Application aliases
*/
app.alias({
'@fonts': 'resources/fonts',
'@images': 'resources/images',
'@scripts': 'resources/scripts',
'@styles': 'resources/styles',
'@fonts': app.path('@fonts'),
'@images': app.path('@images'),
'@scripts': app.path('@scripts'),
'@styles': app.path('@styles'),
})

/**
Expand Down
4 changes: 0 additions & 4 deletions tests/unit/bud-framework/path.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,4 @@ describe('bud.path', function () {
it('path: returns correct paths joined to context', () => {
expect(bud.path('foo')).toEqual(join(mockProject.path, 'foo'))
})

it('path: returns correct paths with @ shorthand', () => {
expect(bud.path('foo')).toContain(`util/project/foo`)
})
})
20 changes: 10 additions & 10 deletions tests/unit/bud-framework/setPath.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {Bud, factory} from '@repo/test-kit/bud'
import {Bud, factory, mockProject} from '@repo/test-kit/bud'
import {resolve} from 'path'

const NEW_PATH = `foo`

describe('bud.setPath', function () {
let bud: Bud

beforeAll(async () => {
beforeEach(async () => {
bud = await factory()
})

Expand All @@ -22,20 +23,19 @@ describe('bud.setPath', function () {
expect(bud.path('@src')).toContain(NEW_PATH)
})

it('sets a path with @ shortcut', () => {
bud.setPath('@src', NEW_PATH)
expect(bud.path('@src')).toContain(NEW_PATH)
})

it('sets multiple paths', () => {
const value = {
src: NEW_PATH,
'@src': NEW_PATH,
'@dist': 'bar',
}

bud.setPath(value)

expect(bud.path('@src')).toEqual(bud.path(value.src))
expect(bud.path('@dist')).toEqual(bud.path(value['@dist']))
expect(bud.path('@src')).toEqual(
resolve(mockProject.path, value['@src']),
)
expect(bud.path('@dist')).toEqual(
resolve(mockProject.path, value['@dist']),
)
})
})

0 comments on commit b854f95

Please sign in to comment.