Skip to content

Commit

Permalink
feat(install): workspace paths globbing and installing localDependencies
Browse files Browse the repository at this point in the history
Plus additional configurations. release-npm
  • Loading branch information
tobua committed Apr 27, 2024
1 parent 6a64957 commit 5412c0e
Show file tree
Hide file tree
Showing 12 changed files with 112 additions and 17 deletions.
1 change: 1 addition & 0 deletions configuration/gitignore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Template } from '../types'

export const templates: Template<string[]> = {
recommended: ['node_modules', 'bun.lockb'],
bundle: ['node_modules', 'bun.lockb', 'dist'],
}

export function createFile(values: string[]) {
Expand Down
14 changes: 14 additions & 0 deletions configuration/rsbuild.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
import { fileExtension, state } from '../state'
import type { Template } from '../types'

export const templates: Template<object> = {
web: {
tools: {
rspack: {
resolve: {
// Resolve absolute imports relative to project root first.
modules: ['.', 'node_modules'],
},
},
},
},
}

export const extension = (path: string) => ({ extends: path })

Expand Down
13 changes: 13 additions & 0 deletions configuration/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,22 @@ export const templates = {
compilerOptions: {
strict: true,
skipLibCheck: true,
verbatimModuleSyntax: true,
target: 'ES2020',
lib: ['DOM', 'ES2020'],
module: 'Preserve',
noEmit: true,
},
},
web: {
compilerOptions: {
skipLibCheck: true,
baseUrl: '.',
target: 'ESNext',
lib: ['DOM', 'ESNext'],
module: 'Preserve',
jsx: 'react-jsx',
noEmit: true,
},
},
}
Expand Down
2 changes: 1 addition & 1 deletion configuration/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const templates: Template<object> = {
'prettier-eslint': {
'editor.defaultFormatter': 'esbenp.prettier-vscode',
'editor.codeActionsOnSave': {
'source.fixAll.eslint': true,
'source.fixAll.eslint': 'always',
},
'editor.formatOnSave': true,
},
Expand Down
48 changes: 38 additions & 10 deletions helper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { existsSync, lstatSync } from 'node:fs'
import { existsSync, lstatSync, symlinkSync } from 'node:fs'
import { it } from 'avait'
import Bun, { Glob } from 'bun'
import Bun from 'bun'
import glob from 'fast-glob'
import { create } from 'logua'
import { parse } from 'parse-gitignore'
import { z } from 'zod'
Expand Down Expand Up @@ -28,21 +29,20 @@ export const validate = (configuration: unknown) => {
}

export async function findConfiguration() {
const packageJson = await Bun.file(root('./package.json')).json()
const { value: typeScriptModuleContents } = await it(import(root('./configuration.ts')))
const { value: javaScriptModuleContents } = await it(import(root('./configuration.js')))

if (!(typeScriptModuleContents || javaScriptModuleContents || Object.hasOwn(packageJson, 'configuration'))) {
if (!(typeScriptModuleContents || javaScriptModuleContents || Object.hasOwn(state.packageJson, 'configuration'))) {
log('No configuration found', 'error')
}

if (!packageJson.configuration && typeScriptModuleContents) {
if (!state.packageJson.configuration && typeScriptModuleContents) {
state.language = 'typescript'
} else if (!packageJson.configuration && javaScriptModuleContents) {
} else if (!state.packageJson.configuration && javaScriptModuleContents) {
state.language = 'javascript'
}

const userConfiguration = packageJson.configuration ?? typeScriptModuleContents ?? javaScriptModuleContents
const userConfiguration = state.packageJson.configuration ?? typeScriptModuleContents ?? javaScriptModuleContents
validate(userConfiguration)
state.options = userConfiguration
}
Expand Down Expand Up @@ -95,9 +95,15 @@ export async function getWorkspaces() {

if (Array.isArray(packageJson.workspaces)) {
for (const workspaceGlob of packageJson.workspaces) {
const glob = new Glob(workspaceGlob)
for await (const file of glob.scan({ cwd: root('/'), dot: false, onlyFiles: false })) {
if (lstatSync(file).isDirectory()) {
const files = await glob(workspaceGlob, {
cwd: root('/'),
dot: false,
onlyFiles: false,
followSymbolicLinks: false,
ignore: ['node_modules'],
})
for await (const file of files) {
if (lstatSync(root(file)).isDirectory()) {
workspaces.push({ path: file, root: false })
}
}
Expand All @@ -108,3 +114,25 @@ export async function getWorkspaces() {

return workspaces
}

export function installLocalDependencies() {
const { localDependencies } = state.packageJson
if (!localDependencies || typeof localDependencies !== 'object' || Object.keys(localDependencies).length === 0) {
return
}

for (const [name, folder] of Object.entries(localDependencies)) {
const absolutePath = root(folder)
const targetPath = root(`node_modules/${name}`)
if (existsSync(absolutePath) && !existsSync(targetPath)) {
try {
symlinkSync(absolutePath, targetPath)
} catch (_error) {
// Symlinks only allowed for administrators on Windows.
log(`Failed to create symlink for localDependency ${name}`, 'warning')
}
} else if (!existsSync(absolutePath)) {
log(`localDependency "${name}" is pointing to a non-existing location: ${absolutePath}`, 'warning')
}
}
}
7 changes: 5 additions & 2 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env bun
import Bun from 'bun'
import { configurations } from './configuration'
import { findConfiguration, getWorkspaces, log, writeGitIgnore } from './helper'
import { findConfiguration, getWorkspaces, installLocalDependencies, log, writeGitIgnore } from './helper'
import { parse } from './parse'
import { reset, root, state } from './state'

Expand All @@ -24,9 +24,12 @@ async function configureProject() {
if (!gitUserConfigured && ignores.length === 0) {
log('No configuration to add', 'warning')
}

installLocalDependencies()
}

for (const workspace of await getWorkspaces()) {
reset(workspace)
const packageJson = await Bun.file(root('./package.json')).json()
reset(workspace, packageJson)
await configureProject()
}
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"dependencies": {
"avait": "^1.0.0",
"fast-glob": "^3.3.2",
"logua": "^3.0.3",
"parse-gitignore": "^2.0.0",
"ts-deepmerge": "^7.0.0",
Expand Down Expand Up @@ -59,6 +60,9 @@
"!test/fixture/workspaces/demo/react",
"test/fixture/workspaces/demo/react/*",
"!test/fixture/workspaces/demo/react/package.json",
"!test/fixture/local-dependencies/node_modules",
"test/fixture/local-dependencies/node_modules/*",
"!test/fixture/local-dependencies/node_modules/keep",
"!test/fixture/*/configuration.ts",
"!test/fixture/*/configuration.js"
],
Expand Down
6 changes: 3 additions & 3 deletions state.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { join } from 'node:path'
import type { State } from './types'
import type { PackageJson, State } from './types'

export const state: State = {
options: {},
Expand All @@ -14,10 +14,10 @@ export const fileExtension = () => (state.language === 'javascript' ? 'js' : 'ts
export const root = (file: string) =>
process.cwd().includes('node_modules') ? join(process.cwd(), '../..', state.directory, file) : join(process.cwd(), state.directory, file)

export const reset = ({ path, root }: { path: string; root: boolean }) => {
export const reset = ({ path, root }: { path: string; root: boolean }, packageJson: PackageJson) => {
state.options = {}
state.language = 'json'
state.packageJson = { name: 'missing-package-name' }
state.packageJson = packageJson
state.directory = path
state.root = root
}
13 changes: 13 additions & 0 deletions test/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,16 @@ test('Creates configuration files in all workspaces including the root.', () =>
expect(existsSync(join(fixturePath, 'demo/react/tsconfig.json'))).toBe(true)
expect(existsSync(join(fixturePath, 'plugin/tsconfig.json'))).toBe(true)
})

test('Will also install local dependencies if listed.', () => {
const fixturePath = './test/fixture/local-dependencies'

execSync('bun ./../../../index.ts', {
cwd: fixturePath,
stdio: 'inherit',
})

expect(existsSync(join(fixturePath, 'tsconfig.json'))).toBe(true)
expect(existsSync(join(fixturePath, 'node_modules/keep/package.json'))).toBe(true)
expect(existsSync(join(fixturePath, 'node_modules/empty-dependency/package.json'))).toBe(true)
})

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

9 changes: 9 additions & 0 deletions test/fixture/local-dependencies/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "local-dependencies",
"localDependencies": {
"empty-dependency": "../empty"
},
"configuration": {
"typescript": "web"
}
}
9 changes: 8 additions & 1 deletion types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import type { ConfigurationKeys } from './configuration'

export type Template<T> = { [key: string]: T | (() => T) }

export type PackageJson = { name: string; author?: string | { name: string } }
type Dependencies = { [key: string]: string }

export type PackageJson = {
name: string
author?: string | { name: string }
localDependencies?: Dependencies
configuration?: { [key: string]: string | object | string[] }
}

export type Configuration = {
name: ConfigurationKeys
Expand Down

0 comments on commit 5412c0e

Please sign in to comment.