Skip to content
This repository was archived by the owner on Jan 15, 2025. It is now read-only.

Commit 69ea5d0

Browse files
Chris McConnellmunozemilio
andauthored
Add --imports switch and improve exported assets copying (#980)
* Add --imports switch. Copy whole sub-directory of ExportedAssets including npm. * Always put in ImportedAssets * Ensure uischema keys are sorted. Co-authored-by: Emilio Munoz <[email protected]>
1 parent a020a62 commit 69ea5d0

File tree

11 files changed

+46
-69
lines changed

11 files changed

+46
-69
lines changed

packages/dialog/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ _See code: [src/commands/dialog/index.ts](https://github.com/microsoft/botframew
3636

3737
## `bf dialog:merge PATTERNS`
3838

39-
Merge `<kind>.schema` and `<kind>[.<locale>].uischema` definitions from a project and its dependencies into a single .schema for describing .dialog files and a per locale .uischema for describing how Composer shows them. For C#, ensures all nuget declarative resources in ExportedAssets are copied to ImportedAssets in the same location.
39+
Merge `<kind>.schema` and `<kind>[.<locale>].uischema` definitions from a project and its dependencies into a single .schema for describing .dialog files and a per locale .uischema for describing how Composer shows them. If a dependent package has an ExportedAssets directory it is copied to ImportedAssets/<package> in the --imports directory.
4040

4141
```
4242
USAGE
@@ -52,6 +52,9 @@ OPTIONS
5252
-v, --verbose Show verbose logging of files as they are processed.
5353
--extension=extension [default: .dialog,.lg,.lu,.schema,.qna,.uischema] Extension to include as a resource for C#.
5454
55+
--imports=imports Output path for imported assets. Defaults to the directory of --out with an ImportedAssets
56+
directory.
57+
5558
EXAMPLES
5659
$ bf dialog:merge myProject.csproj plugins/*.nuspec
5760
$ bf dialog:merge package.json -o app.schema

packages/dialog/src/commands/dialog/merge.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {Command, flags} from '@microsoft/bf-cli-command'
77
import SchemaMerger from '../../library/schemaMerger'
88

99
export default class DialogMerge extends Command {
10-
static description = 'Merge `<kind>.schema` and `<kind>[.<locale>].uischema` definitions from a project and its dependencies into a single .schema for describing .dialog files and a per locale .uischema for describing how Composer shows them. For C#, ensures all nuget declarative resources in ExportedAssets are copied to ImportedAssets in the same location.'
10+
static description = 'Merge `<kind>.schema` and `<kind>[.<locale>].uischema` definitions from a project and its dependencies into a single .schema for describing .dialog files and a per locale .uischema for describing how Composer shows them. If a dependent package has an ExportedAssets directory it is copied to ImportedAssets/<package> in the --imports directory.'
1111

1212
static args = [
1313
{name: 'patterns', required: true, description: 'Any number of glob regex patterns to match .csproj, .nuspec or package.json files.'},
@@ -20,6 +20,7 @@ export default class DialogMerge extends Command {
2020
extension: flags.string({description: 'Extension to include as a resource for C#.', required: false, multiple: true, default: ['.dialog', '.lg', '.lu', '.schema', '.qna', '.uischema']}),
2121
help: flags.help({char: 'h'}),
2222
nugetRoot: flags.string({description: 'Nuget root directory for debugging.', hidden: true}),
23+
imports: flags.string({description: 'Output path for imported assets. Defaults to the directory of --out with an ImportedAssets directory.', required: false}),
2324
output: flags.string({char: 'o', description: 'Output path and filename for merged .schema and .uischema. Defaults to first project name.', required: false}),
2425
schema: flags.string({char: 's', description: 'Path to merged .schema file to use if merging .uischema only.', required: false}),
2526
verbose: flags.boolean({char: 'v', description: 'Show verbose logging of files as they are processed.', default: false}),
@@ -32,7 +33,7 @@ export default class DialogMerge extends Command {
3233

3334
async run() {
3435
const {argv, flags} = this.parse(DialogMerge)
35-
let merger = new SchemaMerger(argv, flags.output, flags.verbose, this.log, this.warn, this.error, flags.extension, flags.schema, flags.debug, flags.nugetRoot)
36+
let merger = new SchemaMerger(argv, flags.output, flags.imports, flags.verbose, this.log, this.warn, this.error, flags.extension, flags.schema, flags.debug, flags.nugetRoot)
3637
await merger.merge()
3738
}
3839
}

packages/dialog/src/library/schemaMerger.ts

Lines changed: 24 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ function normalize(path: string): string {
6060

6161
// Deep merge of JSON objects
6262
function mergeObjects(obj1: any, obj2: any): any {
63-
let target = {};
63+
let target = {}
6464
let merger = (obj: any) => {
6565
for (let prop in obj) {
6666
let val = obj[prop]
@@ -156,6 +156,11 @@ class Component {
156156
return patterns
157157
}
158158

159+
// Test to see if root component
160+
public isRoot(): boolean {
161+
return this.parents.length === 0 || (this.parents.length === 1 && this.parents[0].parents.length === 0)
162+
}
163+
159164
// Check to see if this component is a parent of another component
160165
public isParent(node: Component): boolean {
161166
let found = false
@@ -201,6 +206,7 @@ export default class SchemaMerger {
201206
// Input parameters
202207
private readonly patterns: string[]
203208
private output: string
209+
private readonly imports: string
204210
private readonly verbose: boolean
205211
private readonly log: any
206212
private readonly warn: any
@@ -257,6 +263,7 @@ export default class SchemaMerger {
257263
* Merger to combine component .schema and .uischema files to make a custom schema.
258264
* @param patterns Glob patterns for the .csproj or packge.json files to combine.
259265
* @param output The output file to create or empty string to use default.
266+
* @param imports The output directory for imports.
260267
* @param verbose True to show files as processed.
261268
* @param log Logger for informational messages.
262269
* @param warn Logger for warning messages.
@@ -266,9 +273,10 @@ export default class SchemaMerger {
266273
* @param debug Generate debug output.
267274
* @param nugetRoot Root directory for nuget packages. (Useful for testing.)
268275
*/
269-
public constructor(patterns: string[], output: string, verbose: boolean, log: any, warn: any, error: any, extensions?: string[], schema?: string, debug?: boolean, nugetRoot?: string) {
276+
public constructor(patterns: string[], output: string, imports: string | undefined, verbose: boolean, log: any, warn: any, error: any, extensions?: string[], schema?: string, debug?: boolean, nugetRoot?: string) {
270277
this.patterns = patterns
271278
this.output = output ? ppath.join(ppath.dirname(output), ppath.basename(output, ppath.extname(output))) : ''
279+
this.imports = imports ?? ppath.dirname(output)
272280
this.verbose = verbose
273281
this.log = log
274282
this.warn = warn
@@ -565,7 +573,10 @@ export default class SchemaMerger {
565573
}
566574
if (!this.failed) {
567575
for (let locale of Object.keys(result)) {
568-
let uischema = {$schema: this.metaUISchemaId, ...result[locale]}
576+
let uischema = {$schema: this.metaUISchemaId}
577+
for (let key of Object.keys(result[locale]).sort()) {
578+
uischema[key] = result[locale][key]
579+
}
569580
this.currentFile = ppath.join(ppath.dirname(this.output), outputName + (locale ? '.' + locale : '') + '.uischema')
570581
this.log(`Writing ${this.currentFile}`)
571582
await fs.writeJSON(this.currentFile, uischema, this.jsonOptions)
@@ -587,39 +598,17 @@ export default class SchemaMerger {
587598
return [kindName, locale]
588599
}
589600

590-
// For C# copy all assets into generated/<package>/
601+
// Copy all exported assets into imported assets
591602
private async copyAssets(): Promise<void> {
592-
if (!this.failed && !this.schemaPath) {
593-
let isCS = false
603+
if (!this.failed && !this.schemaPath && this.components.length > 0) {
604+
this.log(`Copying exported assets to ${this.imports}`)
594605
for (let component of this.components) {
595-
if (component.path.endsWith('.csproj') || component.path.endsWith('.nuspec')) {
596-
isCS = true
597-
break
598-
}
599-
}
600-
if (isCS) {
601-
let generatedPath = ppath.join(ppath.dirname(this.output), 'ImportedAssets')
602-
let found = false
603-
for (let files of this.files.values()) {
604-
for (let componentPaths of files.values()) {
605-
for (let componentPath of componentPaths) {
606-
let component = componentPath.component
607-
let path = componentPath.path
608-
let relativePath = ppath.relative(ppath.dirname(component.path), path)
609-
// Copy anything found in exportedassets outside of project
610-
if (!component.isCSProject() && relativePath.toLowerCase().startsWith('exportedassets')) {
611-
// Copy package files to output
612-
if (!found) {
613-
found = true
614-
this.log(`Copying C# package exported assets to ${generatedPath}`)
615-
}
616-
let remaining = relativePath.substring('exportedAssets/'.length)
617-
let outputPath = ppath.join(generatedPath, componentPath.component.name, remaining)
618-
this.vlog(`Copying ${path} to ${outputPath}`)
619-
await fs.ensureDir(ppath.dirname(outputPath))
620-
await fs.copyFile(path, outputPath)
621-
}
622-
}
606+
if (!component.isRoot()) {
607+
let exported = ppath.join(ppath.dirname(component.path), 'exportedAssets')
608+
if (await fs.pathExists(exported)) {
609+
let imported = ppath.join(this.imports, 'ImportedAssets', component.name)
610+
this.vlog(`Copying ${exported} to ${imported}`)
611+
await fs.copy(exported, imported, {recursive: true})
623612
}
624613
}
625614
}
@@ -718,24 +707,6 @@ export default class SchemaMerger {
718707
})
719708
}
720709

721-
// Resolver for schema: -> metaSchema
722-
private schemaProtocolResolver(): any {
723-
let reader = (_file: parser.FileInfo) => {
724-
return JSON.stringify(this.metaSchema)
725-
}
726-
return {
727-
resolve: {
728-
defintion: {
729-
order: 1,
730-
canRead: /^schema:/i,
731-
read(file: parser.FileInfo, _callback: any, _$refs: any) {
732-
return reader(file)
733-
}
734-
}
735-
}
736-
}
737-
}
738-
739710
private indent(): string {
740711
return ' '.repeat(this.parents.length)
741712
}
@@ -917,7 +888,7 @@ export default class SchemaMerger {
917888
let dependentPath = ppath.join(rootDir, 'node_modules', dependent, 'package.json')
918889
if (await fs.pathExists(dependentPath)) {
919890
await this.expandPackageJson(dependentPath)
920-
break;
891+
break
921892
} else {
922893
rootDir = ppath.dirname(rootDir)
923894
}

packages/dialog/test/commands/dialog/merge.test.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ async function merge(patterns: string[], output?: string, verbose?: boolean, sch
3232
}
3333
let merger = new SchemaMerger(patterns,
3434
output ? ppath.join(tempDir, output) : '',
35+
undefined,
3536
verbose || false,
3637
logger, logger, logger,
3738
undefined,
@@ -168,10 +169,8 @@ describe('dialog:merge', async () => {
168169
assert(countMatches(/Following.*project3/, lines) === 1, 'Did not follow project1')
169170
assert(countMatches(/Following nuget.*nuget3.*1.0.0/, lines) === 1, 'Did not follow nuget3')
170171
assert(countMatches(/Parsing.*nuget3.component1.schema/, lines) === 1, 'Missing nuget3.component1.schema')
171-
assert(countMatches(/Copying/i, lines) === 4, 'Wrong number of copies')
172-
assert(countMatches(/Copying.*nuget3.lg/i, lines) === 1, 'Did not copy .lg')
173-
assert(countMatches(/Copying.*nuget3.lu/i, lines) === 1, 'Did not copy .lu')
174-
assert(countMatches(/Copying.*nuget3.qna/i, lines) === 1, 'Did not copy .qna')
172+
assert(countMatches(/Copying/i, lines) === 2, 'Wrong number of copies')
173+
assert(countMatches(/Copying.*nuget3/i, lines) === 1, 'Did not copy nuget3')
175174
assert(await fs.pathExists(ppath.join(tempDir, 'ImportedAssets', 'nuget3', 'stuff', 'nuget3.qna')), 'Did not copy directory')
176175
await compareToOracle('project3.schema')
177176
await compareToOracle('project3.en-us.uischema')
@@ -224,6 +223,9 @@ describe('dialog:merge', async () => {
224223
assert(countMatches('dependent-package.schema', lines) === 1, 'Missing dependent-package.schema')
225224
assert(countMatches('parent-package.schema', lines) === 1, 'Missing parent-package.schema')
226225
assert(countMatches('no-package.schema', lines) === 0, 'Extra no-package.schema')
226+
assert(countMatches('Copying', lines) === 2, 'Wrong number of copies')
227+
assert(await fs.pathExists(ppath.join(tempDir, 'ImportedAssets', 'dependent-package', 'assets', 'dependent-package.jpg')), 'Incomplete assets copy')
228+
assert(!await fs.pathExists(ppath.join(tempDir, 'ImportedAssets', 'root-package')), 'Copied rootx')
227229
await compareToOracle('root-package.schema')
228230
await compareToOracle('root-package.uischema')
229231
})

packages/dialog/test/commands/dialog/npm/node_modules/root-package/exportedAssets/notCopied.dialog

Whitespace-only changes.

packages/dialog/test/commands/dialog/npm/node_modules/root-package/node_modules/dependent-package/exportedAssets/assets/dependent-package.jpg

Loading

packages/dialog/test/commands/dialog/npm/node_modules/root-package/node_modules/dependent-package/exportedAssets/dependent-package.dialog

Whitespace-only changes.

packages/dialog/test/commands/dialog/npm/node_modules/root-package/node_modules/dependent-package/exportedAssets/dependent-package.lg

Whitespace-only changes.

packages/dialog/test/commands/dialog/npm/node_modules/root-package/node_modules/dependent-package/exportedAssets/dependent-package.lu

Whitespace-only changes.

packages/dialog/test/commands/dialog/npm/node_modules/root-package/node_modules/dependent-package/exportedAssets/dependent-package.qna

Whitespace-only changes.

0 commit comments

Comments
 (0)