test-drive a file loader

This commit is contained in:
Loren Norman 2025-07-09 12:57:35 -04:00
parent 6ec9c9183c
commit 1bd02cdda8
3 changed files with 158 additions and 4 deletions

View file

@ -1,5 +1,101 @@
export const DefinitionLoader = {
import { glob } from 'glob'
import { camelCase, fromPairs } from 'lodash-es'
const
PROJECT_ROOT = process.cwd(),
BLOCK_LOCATION = `app/blocks/`,
EXTENSION_LOCATION = `app/extensions/`,
MIXIN_LOCATION = `app/mixins/`,
MUTATOR_LOCATION = `app/mutators/`,
TOOLBOX_LOCATION = `app/toolbox/`,
WORKSPACE_LOCATION = `app/workspace/`,
EXAMPLE_FILES = [ '**/*example*.js' ],
NON_BLOCK_FILES = [
'**/*extension*.js',
'**/*mixin*.js',
'**/*mutator*.js',
'**/*shadow*.js'
].concat(EXAMPLE_FILES)
export const DefinitionLoader = {
loadMutators: async () => {
const jsfiles = await glob(`./${MUTATOR_LOCATION}**/*.js`, { ignore: EXAMPLE_FILES })
// loads app/mutators/mutator_name.js into object like:
// { mutatorName: Function }
return fromPairs(await Promise.all(
jsfiles.map( async filePath => ([
camelCase(filePath.split('/').at(-1).slice(0, -3)),
(await import(`${PROJECT_ROOT}/${filePath}`)).default
]))
))
},
loadMixins: async () => {
const jsfiles = await glob(`./${MIXIN_LOCATION}**/*.js`, { ignore: EXAMPLE_FILES })
// loads app/mixins/mixin_name.js into object like:
// { mixinName: Function }
return fromPairs(await Promise.all(
jsfiles.map( async filePath => ([
camelCase(filePath.split('/').at(-1).slice(0, -3)),
(await import(`${PROJECT_ROOT}/${filePath}`)).default
]))
))
},
loadExtensions: async () => {
const jsfiles = await glob(`./${EXTENSION_LOCATION}**/*.js`, { ignore: EXAMPLE_FILES })
// loads app/extensions/extension_name.js into object like:
// { extensionName: Function }
return fromPairs(await Promise.all(
jsfiles.map( async filePath => ([
camelCase(filePath.split('/').at(-1).slice(0, -3)),
(await import(`${PROJECT_ROOT}/${filePath}`)).default
]))
))
},
loadBlocks: async () => {
// get the file listing
const jsfiles = await glob(`./${BLOCK_LOCATION}**/*.js`, { ignore: NON_BLOCK_FILES })
// load each file
return Promise.all(
jsfiles.map(
async filePath => ({
definition: (await import(`${PROJECT_ROOT}/${filePath}`)).default,
path: filePath.slice(BLOCK_LOCATION.length)
})
)
)
},
loadToolboxes: async () => {
// hardcode to a single toolbox for now
const rawToolboxDef = (await import(`${PROJECT_ROOT}/${TOOLBOX_LOCATION}index.js`)).default
return [ rawToolboxDef ]
},
loadWorkspaces: async () => {
// hardcode to a single workspace for now
const rawWorkspaceDef = (await import(`${PROJECT_ROOT}/${WORKSPACE_LOCATION}workspace.json`, { with: { type: 'json' }})).default
return [ rawWorkspaceDef ]
},
loadAll: async () => {
return {
mutators: await DefinitionLoader.loadMutators(),
mixins: await DefinitionLoader.loadMixins(),
extensions: await DefinitionLoader.loadExtensions(),
blocks: await DefinitionLoader.loadBlocks(),
toolboxes: await DefinitionLoader.loadToolboxes(),
workspaces: await DefinitionLoader.loadWorkspaces(),
}
}
}
export default DefinitionLoader

View file

@ -1,4 +1,3 @@
import util from 'util'
import { glob } from 'glob'
import { assign, camelCase, fromPairs, isArray, isString } from 'lodash-es'

View file

@ -4,7 +4,66 @@ import { assert } from 'chai'
import DefinitionLoader from "#src/definition_loader.js"
describe("DefinitionLoader", () => {
describe("DefinitionLoader", { only: true }, () => {
describe("config")
describe("locating files")
describe("locating files", () => {
it("finds mutator definitions", async () => {
const rawMutatorDefs = await DefinitionLoader.loadMutators()
assert.isAbove(Object.keys(rawMutatorDefs).length, 1)
})
it("finds mixin definitions", async () => {
const rawMixinDefs = await DefinitionLoader.loadMixins()
assert.lengthOf(Object.keys(rawMixinDefs), 1)
})
it("finds extension definitions", async () => {
const rawExtensionDefs = await DefinitionLoader.loadExtensions()
assert.isAbove(Object.keys(rawExtensionDefs).length, 1)
})
it("tracks block definitions and their paths", async () => {
const rawBlockDefs = await DefinitionLoader.loadBlocks()
// many blocks
assert.isAbove(rawBlockDefs.length, 1)
// path is a js file
assert.match(rawBlockDefs[0].path, /\.js$/)
// has a type property
assert.hasAnyKeys(rawBlockDefs[0].definition, ['type'])
})
it("finds toolbox definitions", async () => {
const rawToolboxDefs = await DefinitionLoader.loadToolboxes()
// one toolbox
assert.lengthOf(rawToolboxDefs, 1)
// with 10 items
assert.isAbove(rawToolboxDefs[0].length, 1)
})
it("finds workspace definitions", async () => {
const rawWorkspaceDefs = await DefinitionLoader.loadWorkspaces()
// one workspace
assert.lengthOf(rawWorkspaceDefs, 1)
// defines language version and list of blocks
assert.equal(rawWorkspaceDefs[0].blocks.languageVersion, 0)
assert.lengthOf(rawWorkspaceDefs[0].blocks.blocks, 1)
})
})
describe("loading all files", () => {
it("should have all resource types", async () => {
const definitions = await DefinitionLoader.loadAll()
assert.isAbove(Object.keys(definitions.mutators).length, 1)
assert.lengthOf(Object.keys(definitions.mixins), 1)
assert.isAbove(Object.keys(definitions.extensions).length, 1)
assert.isAbove(definitions.blocks.length, 1)
assert.lengthOf(definitions.toolboxes, 1)
assert.lengthOf(definitions.workspaces, 1)
})
})
})