-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cli): use new action factory definitions
- Loading branch information
1 parent
26adcfd
commit 174aabc
Showing
21 changed files
with
537 additions
and
256 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,114 @@ | ||
import renderExport from '@foscia/cli/templates/renderExport'; | ||
import renderImport from '@foscia/cli/templates/renderImport'; | ||
import renderImportsList from '@foscia/cli/templates/renderImportsList'; | ||
import { CLIConfig } from '@foscia/cli/utils/config/config'; | ||
import type { ActionFactoryOptions } from '@foscia/cli/utils/input/promptForActionFactoryOptions'; | ||
import { sortBy } from 'lodash-es'; | ||
import { ImportsList } from '@foscia/cli/utils/imports/makeImportsList'; | ||
import type { | ||
ActionFactoryDependency, | ||
ActionFactoryModelRegistration, | ||
ActionFactoryOptions, | ||
} from '@foscia/cli/utils/input/promptForActionFactoryOptions'; | ||
import toIndent from '@foscia/cli/utils/output/toIndent'; | ||
|
||
type ActionFactoryTemplateData = { | ||
config: CLIConfig; | ||
imports: ImportsList; | ||
usage: CLIConfig['usage']; | ||
options: ActionFactoryOptions; | ||
}; | ||
|
||
export function renderModelsDefinition({ config, options }: ActionFactoryTemplateData) { | ||
if (options.automaticRegistration === 'import.meta.glob') { | ||
const modelsCast = config.language === 'ts' | ||
? ' as { [k: string]: Model }' | ||
: ''; | ||
export function renderModelsRegistration( | ||
{ config, registry }: { config: CLIConfig; registry: ActionFactoryModelRegistration; }, | ||
) { | ||
let typeAssertion = ''; | ||
if (registry === 'import.meta.glob') { | ||
if (config.language === 'ts') { | ||
typeAssertion = ' as { [k: string]: Model }'; | ||
} | ||
|
||
return ` | ||
const models = Object.values(import.meta.glob('./models/*.${config.language}', { | ||
import: 'default', eager: true, | ||
})${modelsCast}); | ||
${toIndent(config, 'import: \'default\', eager: true,')} | ||
})${typeAssertion}); | ||
`.trim(); | ||
} | ||
|
||
if (registry === 'require.context') { | ||
if (config.language === 'ts') { | ||
typeAssertion = ' as Model'; | ||
} | ||
|
||
return ` | ||
const modelsRequireContext = require.context('./models', /\\.${config.language}/); | ||
const models = modelsRequireContext.keys().map( | ||
${toIndent(config, `(key) => modelsRequireContext(key).default${typeAssertion}`)}, | ||
); | ||
`.trim(); | ||
} | ||
|
||
return 'const models = [/* TODO Post, Comment, [...] */];'; | ||
} | ||
|
||
function renderBlueprintActionFactory({ config, usage, options }: ActionFactoryTemplateData) { | ||
const enableModelFeatures = ['jsonapi', 'jsonrest'].indexOf(usage) !== -1; | ||
const factoryFunction = { | ||
jsonapi: { name: 'makeJsonApi', package: 'jsonapi' }, | ||
jsonrest: { name: 'makeJsonRest', package: 'rest' }, | ||
http: { name: 'makeHttpClient', package: 'http' }, | ||
}[usage]; | ||
function renderFactoryOptions(config: CLIConfig, options?: { [K: string]: unknown }) { | ||
const emptyOptions = Object.values(options ?? {}).filter((o) => o !== undefined).length === 0; | ||
if (emptyOptions) { | ||
return ''; | ||
} | ||
|
||
const factoryConfiguration = { | ||
...(enableModelFeatures ? { models: '____models____' } : {}), | ||
...options.config, | ||
}; | ||
const factoryConfigured = Object.values(factoryConfiguration) | ||
.filter((o) => o !== undefined) | ||
.length > 0; | ||
const factoryConfigurationJsonLiteral = factoryConfigured | ||
? JSON.stringify(factoryConfiguration, null, config.tabSize ?? 2).replace(/"([^"]+)":/g, '$1:') | ||
: ''; | ||
const factoryConfigurationLiteral = factoryConfigurationJsonLiteral | ||
return JSON.stringify(options, null, (config.tabSize ?? 2)) | ||
.replace(/"([^"]+)":/g, '$1:') | ||
.replace(/"____([a-z]+)____"/, '$1') | ||
.replace('models: models,', 'models,'); | ||
|
||
const blueprintImportStatement = renderImport({ | ||
config, | ||
name: `{ ${factoryFunction.name} }`, | ||
from: `@foscia/${factoryFunction.package}`, | ||
}); | ||
.replace(/\\"/g, '\\\'') | ||
.replace(/"/g, '\''); | ||
} | ||
|
||
const coreImports = [] as string[]; | ||
if (enableModelFeatures && options.automaticRegistration) { | ||
coreImports.push('Model'); | ||
} | ||
function renderFactoryDependency( | ||
config: CLIConfig, | ||
comment: string, | ||
dependency?: ActionFactoryDependency, | ||
) { | ||
return dependency | ||
? `${comment}\n...${dependency.name}(${renderFactoryOptions(config, dependency.options)}),` | ||
: undefined; | ||
} | ||
|
||
const coreImportStatement = coreImports.length | ||
? `\n${renderImport({ | ||
config, | ||
name: `{ ${sortBy(coreImports).join(', ')} }`, | ||
from: '@foscia/core', | ||
})}` | ||
export default function renderActionFactory( | ||
{ config, imports, options }: ActionFactoryTemplateData, | ||
) { | ||
const modelsRegistration = options.registry | ||
? `\n${renderModelsRegistration({ config, registry: options.registry })}\n` | ||
: ''; | ||
|
||
return ` | ||
${blueprintImportStatement}${coreImportStatement} | ||
${enableModelFeatures ? `\n${renderModelsDefinition({ config, usage, options })}\n` : ''} | ||
const { action } = ${factoryFunction.name}(${factoryConfigurationLiteral}); | ||
const actionFactoryDependencies = [ | ||
options.cache | ||
? '// Cache stores already retrieved models\' instances\n// and avoid duplicates records to coexists.\n// If you don\'t care about this feature, you can remove it.\n...makeCache(),' | ||
: undefined, | ||
options.registry | ||
? '// Registry stores a map of type string and models classes.\n// You have this dependency because you\'ve opt-in for it.\n...makeRegistry(models),' | ||
: undefined, | ||
renderFactoryDependency( | ||
config, | ||
'// Deserializer transforms data source\'s raw data to model\'s instances.\n// If you don\'t retrieve models from your data store, you can remove it.', | ||
options.deserializer, | ||
), | ||
renderFactoryDependency( | ||
config, | ||
'// Serializer transforms model\'s instances to your data source\'s format.\n// If you don\'t send models to your data store, you can remove it.', | ||
options.serializer, | ||
), | ||
renderFactoryDependency( | ||
config, | ||
'// Adapter exchanges data with your data source.\n// This is mandatory when using Foscia.', | ||
options.adapter, | ||
), | ||
]; | ||
|
||
${renderExport({ config, expr: 'action' })} | ||
const actionFactory = ` | ||
makeActionFactory({ | ||
${actionFactoryDependencies.filter((d) => d).map((d) => toIndent(config, d!)).join('\n')} | ||
}) | ||
`.trim(); | ||
} | ||
|
||
export default function renderActionFactory({ config, usage, options }: ActionFactoryTemplateData) { | ||
return renderBlueprintActionFactory({ config, usage, options }); | ||
return ` | ||
${renderImportsList({ config, imports })}${modelsRegistration} | ||
${renderExport({ config, expr: actionFactory })} | ||
`.trim(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,51 @@ | ||
import { CLIConfig } from '@foscia/cli/utils/config/config'; | ||
import renderComposableForDef from '@foscia/cli/templates/renderComposableForDef'; | ||
import renderExport from '@foscia/cli/templates/renderExport'; | ||
import renderImport from '@foscia/cli/templates/renderImport'; | ||
import renderImportsList from '@foscia/cli/templates/renderImportsList'; | ||
import renderPropertyForDef from '@foscia/cli/templates/renderPropertyForDef'; | ||
import { CLIConfig } from '@foscia/cli/utils/config/config'; | ||
import { ImportsList } from '@foscia/cli/utils/imports/makeImportsList'; | ||
import { DefinitionProperty } from '@foscia/cli/utils/input/promptForProperties'; | ||
import toIndent from '@foscia/cli/utils/output/toIndent'; | ||
import toJoinMultiline from '@foscia/cli/utils/output/toJoinMultiline'; | ||
import { MakeProperty, MakeType } from '@foscia/cli/utils/make'; | ||
import { sortBy, uniq } from 'lodash-es'; | ||
import { uniq } from 'lodash-es'; | ||
|
||
type ComposableTemplateData = { | ||
config: CLIConfig; | ||
composables: MakeType[]; | ||
properties: MakeProperty[]; | ||
imports: ImportsList; | ||
composables: string[]; | ||
properties: DefinitionProperty[]; | ||
}; | ||
|
||
export function renderFosciaImports( | ||
{ config, properties, name }: { config: CLIConfig, properties: MakeProperty[], name: string }, | ||
) { | ||
return renderImport({ | ||
config, | ||
name: [name, ...properties.map((p) => p.typology)], | ||
from: '@foscia/core', | ||
}); | ||
} | ||
|
||
export function renderDefinitionImports( | ||
{ config, types }: { config: CLIConfig, types: (MakeProperty | MakeType)[] }, | ||
context: 'models' | 'composables', | ||
) { | ||
return toJoinMultiline(sortBy(uniq( | ||
types | ||
.map((p) => { | ||
const isProperty = 'type' in p; | ||
const type = (isProperty ? p.type : p) as MakeType; | ||
|
||
return renderImport({ | ||
config, | ||
name: type?.name, | ||
from: type?.from, | ||
typeOnly: isProperty, | ||
context, | ||
}); | ||
}) | ||
.filter((i) => i) as string[], | ||
))); | ||
} | ||
|
||
export function renderDefinition( | ||
{ config, composables, properties }: { | ||
config: CLIConfig; | ||
composables: MakeType[]; | ||
properties: MakeProperty[]; | ||
composables: string[]; | ||
properties: DefinitionProperty[]; | ||
}, | ||
) { | ||
const definition = (composables.length + properties.length) | ||
? toJoinMultiline(uniq([ | ||
? `${uniq([ | ||
...composables.map( | ||
(c) => `${toIndent(config)}${renderComposableForDef({ composable: c })}`, | ||
(composable) => toIndent(config, renderComposableForDef({ composable })), | ||
), | ||
...properties.map( | ||
(p) => `${toIndent(config)}${renderPropertyForDef({ property: p })}`, | ||
(property) => toIndent(config, renderPropertyForDef({ property })), | ||
), | ||
]), ',\n') | ||
: `${toIndent(config)}// TODO Write definition.\n`; | ||
]).join(',\n')},` | ||
: `${toIndent(config, '// TODO Write definition.')}`; | ||
|
||
return `{\n${definition}}`.trim(); | ||
return `{\n${definition}\n}`.trim(); | ||
} | ||
|
||
export default function renderComposable( | ||
{ config, composables, properties }: ComposableTemplateData, | ||
{ config, imports, composables, properties }: ComposableTemplateData, | ||
) { | ||
const composableDef = renderDefinition({ config, composables, properties }); | ||
const composableObject = `makeComposable(${composableDef})`; | ||
const composableTypes = [...composables, ...properties]; | ||
|
||
imports.add('makeComposable', '@foscia/core'); | ||
|
||
return ` | ||
${renderFosciaImports({ config, properties, name: 'makeComposable' })} | ||
${renderDefinitionImports({ config, types: composableTypes }, 'composables')} | ||
${renderImportsList({ config, imports, context: 'composables' })} | ||
${renderExport({ config, expr: composableObject })} | ||
`.trim(); | ||
} |
Oops, something went wrong.