فهرست منبع

added basic {{template}} handling to buildutils

telamonian 5 سال پیش
والد
کامیت
9929092e0a
2فایلهای تغییر یافته به همراه99 افزوده شده و 22 حذف شده
  1. 36 22
      buildutils/src/ensure-package.ts
  2. 63 0
      buildutils/src/utils.ts

+ 36 - 22
buildutils/src/ensure-package.ts

@@ -19,11 +19,19 @@ const HEADER_TEMPLATE = `
 /* This file was auto-generated by {{funcName}}() in @jupyterlab/buildutils */
 `;
 
-function generatedHeader(funcName: string): string {
-  return HEADER_TEMPLATE.split('{{funcName}}')
-    .join(funcName)
-    .trim();
+const ICON_IMPORTS_TEMPLATE = `
+import { Icon } from './interfaces';
+
+// icon svg import statements
+{{iconImportStatements}}
+
+// defaultIcons definition
+export namespace IconImports {
+  export const defaultIcons: ReadonlyArray<Icon.IModel> = [
+    {{iconModelDeclarations}}
+  ];
 }
+`;
 
 /**
  * Ensure the integrity of a package.
@@ -144,7 +152,12 @@ export async function ensurePackage(
 
   // Template the CSS index file.
   if (cssImports && fs.existsSync(path.join(pkgPath, 'style/base.css'))) {
-    let cssIndex = generatedHeader('ensurePackage');
+    const funcName = 'ensurePackage';
+    let cssIndex = utils.fromTemplate(
+      HEADER_TEMPLATE,
+      { funcName },
+      { end: '' }
+    );
     cssImports.forEach(cssImport => {
       cssIndex += `\n@import url('~${cssImport}');`;
     });
@@ -307,7 +320,7 @@ export async function ensurePackage(
  */
 export async function ensureUiComponents(pkgPath: string): Promise<string[]> {
   let messages: string[] = [];
-  const tab = '  ';
+  // const tab = '  ';
 
   const iconSrcDir = path.join(pkgPath, 'src/icon');
   const svgs = glob.sync(path.join(pkgPath, 'style/icons', '**/*.svg'));
@@ -321,25 +334,26 @@ export async function ensureUiComponents(pkgPath: string): Promise<string[]> {
     iconImportStatements.push(
       `import ${nameCamel} from '${path.relative(iconSrcDir, svg)}';`
     );
-    iconModelDeclarations.push(
-      tab + tab + `{ name: '${name}', svg: ${nameCamel} }`
-    );
+    iconModelDeclarations.push(`{ name: '${name}', svg: ${nameCamel} }`);
   });
 
   // generate the actual iconImports file
-  let iconImports = generatedHeader('ensureUiComponents') + '\n\n';
-  iconImports += "import { Icon } from './interfaces';\n\n";
-
-  iconImports += '// icon svg import statements\n';
-  iconImports += iconImportStatements.join('\n') + '\n\n';
-
-  iconImports += '// defaultIcons definition\n';
-  iconImports += 'export namespace IconImports {\n';
-  iconImports +=
-    tab + 'export const defaultIcons: ReadonlyArray<Icon.IModel> = [\n';
-  iconImports += iconModelDeclarations.join(',\n') + '\n';
-  iconImports += tab + '];\n';
-  iconImports += '}\n';
+  /* tslint:disable */
+  const funcName = 'ensureUiComponents';
+  let iconImports = utils.fromTemplate(
+    HEADER_TEMPLATE,
+    { funcName },
+    { end: '\n\n' }
+  );
+  iconImports += utils.fromTemplate(ICON_IMPORTS_TEMPLATE, {
+    iconImportStatements
+  });
+  iconImports = utils.fromTemplate(
+    iconImports,
+    { iconModelDeclarations },
+    { join: ',\n' }
+  );
+  /* tslint:enable */
 
   // write the iconImports file
   const iconImportsPath = path.join(iconSrcDir, 'iconImports.ts');

+ 63 - 0
buildutils/src/utils.ts

@@ -114,6 +114,69 @@ export function writeJSONFile(filePath: string, data: any): boolean {
   return false;
 }
 
+/**
+ * Simple template substitution for template vars of the form {{name}}
+ *
+ * @param templ: the template string.
+ * Ex: `This header generated by {{funcName}}`
+ *
+ * @param subs: an object in which the parameter keys are the template
+ * variables and the parameter values are the substitutions. Each value
+ * can be either a single string or an array of strings. Arrays of strings
+ * will be automatically joined.
+ *
+ * @param options: function options.
+ *
+ * @param options.autoindent: default = true. If true, will try to match
+ * indentation level of {{var}} in substituted template, if the substitution
+ * value is supplied as an array of strings.
+ *
+ * @param options.end: default = '\n'. Inserted at the end of
+ * a template post-substitution.
+ *
+ * @param options.join: default = '\n'. If using array substitution values, this is
+ * the string that will be used to join each array.
+ *
+ * @returns the input template with all {{vars}} substituted.
+ */
+export function fromTemplate(
+  templ: string,
+  subs: Dict<string | string[]>,
+  options: { autoindent?: boolean; end?: string; join?: string } = {}
+) {
+  // default options values
+  const autoindent =
+    options.autoindent === undefined ? true : options.autoindent;
+  const end = options.end === undefined ? '\n' : options.end;
+  const join = options.join === undefined ? '\n' : options.join;
+
+  Object.keys(subs).forEach(key => {
+    if (autoindent && subs[key] instanceof Array) {
+      // if val is an array of strings, assume one line per entry
+      templ = templ.split(`{{${key}}}`).reduce((acc, cur) => {
+        // try to match the indentation level of the {{var}} in the input template
+        let leadingWhiteSpace = acc.match(/\n?(\s*).*$/);
+        return (
+          acc +
+          (subs[key] as string[]).join(
+            join + (leadingWhiteSpace ? leadingWhiteSpace[1] : '')
+          ) +
+          cur
+        );
+      });
+    } else {
+      const val =
+        subs[key] instanceof Array
+          ? (subs[key] as string[]).join(join)
+          : (subs[key] as string);
+
+      templ = templ.split(`{{${key}}}`).join(val);
+    }
+  });
+
+  return templ.trim() + end;
+}
+
 /**
  *
  * Call a command, checking its status.