Example #1
0
export function getSourceFile(host: Tree, path: string): ts.SourceFile {
  const buffer = host.read(path);
  if (!buffer) {
    throw new SchematicsException(`Could not find file for path: ${path}`);
  }
  const content = buffer.toString();
  const source = ts.createSourceFile(
    path,
    content,
    ts.ScriptTarget.Latest,
    true
  );
  return source;
}
Example #2
0
function _readPackageJson(tree: Tree): JsonAstObject {
  const buffer = tree.read(pkgJsonPath);
  if (buffer === null) {
    throw new SchematicsException('Could not read package.json.');
  }
  const content = buffer.toString();

  const packageJson = parseJsonAst(content, JsonParseMode.Strict);
  if (packageJson.kind != 'object') {
    throw new SchematicsException('Invalid package.json. Was expecting an object');
  }

  return packageJson;
}
Example #3
0
/**
 * Resolve the workspace configuration of the specified tree gracefully. We cannot use the utility
 * functions from the default Angular schematics because those might not be present in older
 * versions of the CLI. Also it's important to resolve the workspace gracefully because
 * the CLI project could be still using `.angular-cli.json` instead of thew new config.
 */
function getWorkspaceConfigGracefully(tree: Tree): any {
  const path = defaultWorkspaceConfigPaths.find(filePath => tree.exists(filePath));
  const configBuffer = tree.read(path !);

  if (!path || !configBuffer) {
    return null;
  }

  try {
    return JSON.parse(configBuffer.toString());
  } catch {
    return null;
  }
}
Example #4
0
  return (tree: Tree) => {
    const workspaceConfig = tree.read('/angular.json');
    if (!workspaceConfig) {
      throw new SchematicsException('Could not find Angular workspace configuration');
    }

    // convert workspace to string
    const workspaceContent = workspaceConfig.toString();

    // parse workspace string into JSON object
    const workspace: experimental.workspace.WorkspaceSchema = JSON.parse(workspaceContent);
// #enddocregion workspace
// #docregion project-fallback
    if (!options.project) {
      options.project = workspace.defaultProject;
    }
// #enddocregion project-fallback

// #docregion project-info
    const projectName = options.project as string;

    const project = workspace.projects[projectName];

    const projectType = project.projectType === 'application' ? 'app' : 'lib';
// #enddocregion project-info

// #docregion path
    if (options.path === undefined) {
      options.path = `${project.sourceRoot}/${projectType}`;
    }
// #enddocregion path

// #docregion template
    const templateSource = apply(url('./files'), [
      applyTemplates({
        classify: strings.classify,
        dasherize: strings.dasherize,
        name: options.name
      }),
      move(normalize(options.path as string))
    ]);
// #enddocregion template

// #docregion chain
    return chain([
      mergeWith(templateSource)
    ]);
// #enddocregion chain
// #docregion workspace
  };
Example #5
0
  return (host: Tree, context: SchematicContext) => {
    const workspace = getWorkspace(host);
    const project = workspace.projects[options.project as string];
    let path: string;
    const projectTargets = project.targets || project.architect;
    if (project && projectTargets && projectTargets.build && projectTargets.build.options.index) {
      path = projectTargets.build.options.index;
    } else {
      throw new SchematicsException('Could not find index file for the project');
    }
    const buffer = host.read(path);
    if (buffer === null) {
      throw new SchematicsException(`Could not read index file: ${path}`);
    }
    const content = buffer.toString();
    const lines = content.split('\n');
    let closingHeadTagLineIndex = -1;
    let closingBodyTagLineIndex = -1;
    lines.forEach((line, index) => {
      if (closingHeadTagLineIndex === -1 && /<\/head>/.test(line)) {
        closingHeadTagLineIndex = index;
      } else if (closingBodyTagLineIndex === -1 && /<\/body>/.test(line)) {
        closingBodyTagLineIndex = index;
      }
    });

    const headIndent = getIndent(lines[closingHeadTagLineIndex]) + '  ';
    const itemsToAddToHead = [
      '<link rel="manifest" href="manifest.json">',
      '<meta name="theme-color" content="#1976d2">',
    ];

    const bodyIndent = getIndent(lines[closingBodyTagLineIndex]) + '  ';
    const itemsToAddToBody = [
      '<noscript>Please enable JavaScript to continue using this application.</noscript>',
    ];

    const updatedIndex = [
      ...lines.slice(0, closingHeadTagLineIndex),
      ...itemsToAddToHead.map(line => headIndent + line),
      ...lines.slice(closingHeadTagLineIndex, closingBodyTagLineIndex),
      ...itemsToAddToBody.map(line => bodyIndent + line),
      ...lines.slice(closingHeadTagLineIndex),
    ].join('\n');

    host.overwrite(path, updatedIndex);

    return host;
  };
Example #6
0
  return (host: Tree) => {
    const workspace = getWorkspace(host);
    const project = getProjectFromWorkspace(workspace, options.project);
    const appHTMLFile = `${project.sourceRoot}/app/app.component.html`;
    const buffer = host.read(appHTMLFile);

    if (!buffer) {
      console.log();
      console.error(chalk.red(`Could not find the project ${chalk.blue(appHTMLFile)} file inside of the ` +
        `workspace config`));
      return;
    }
    host.overwrite(appHTMLFile, bootstrapHTML);
    return host;
  };
Example #7
0
function getTsConfigOutDir(host: Tree, tsConfigPath: string): string {
  const tsConfigBuffer = host.read(tsConfigPath);
  if (!tsConfigBuffer) {
    throw new SchematicsException(`Could not read ${tsConfigPath}`);
  }
  const tsConfigContent = tsConfigBuffer.toString();
  const tsConfig = parseJson(tsConfigContent);
  if (tsConfig === null || typeof tsConfig !== 'object' || Array.isArray(tsConfig) ||
    tsConfig.compilerOptions === null || typeof tsConfig.compilerOptions !== 'object' ||
    Array.isArray(tsConfig.compilerOptions)) {
    throw new SchematicsException(`Invalid tsconfig - ${tsConfigPath}`);
  }
  const outDir = tsConfig.compilerOptions.outDir;

  return outDir as string;
}
Example #8
0
File: index.ts Project: iwe7/devkit
  return (host: Tree) => {
    const pkgPath = '/package.json';
    const buffer = host.read(pkgPath);
    if (buffer === null) {
      throw new SchematicsException('Could not find package.json');
    }

    const pkg = JSON.parse(buffer.toString());

    const ngCoreVersion = pkg.dependencies['@angular/core'];
    pkg.dependencies['@angular/platform-server'] = ngCoreVersion;

    host.overwrite(pkgPath, JSON.stringify(pkg, null, 2));

    return host;
  };
Example #9
0
export function addPackageToPackageJson(host: Tree, type: string, pkg: string, version: string) {
  if (host.exists('package.json')) {
    const sourceText = host.read('package.json')!.toString('utf-8');
    const json = JSON.parse(sourceText);
    if (!json[type]) {
      json[type] = {};
    }

    if (!json[type][pkg]) {
      json[type][pkg] = version;
    }

    host.overwrite('package.json', JSON.stringify(json, null, 2));
  }

  return host;
}
Example #10
0
 return (host: Tree) => {
   const gitignore = '/.gitignore';
   if (!host.exists(gitignore)) {
     return host;
   }
   const gitIgnoreContent = host.read(gitignore).toString();
   if (gitIgnoreContent.includes('\n/bazel-out\n')) {
     return host;
   }
   const compiledOutput = '# compiled output\n';
   const index = gitIgnoreContent.indexOf(compiledOutput);
   const insertionIndex = index >= 0 ? index + compiledOutput.length : gitIgnoreContent.length;
   const recorder = host.beginUpdate(gitignore);
   recorder.insertRight(insertionIndex, '/bazel-out\n');
   host.commitUpdate(recorder);
   return host;
 };
Example #11
0
  return (host: Tree) => {
    const workspace = getWorkspace(host);
    const project = getProjectFromWorkspace(workspace, options.project);

    const stylesPath = getStylesPath(host, project);

    const buffer = host.read(stylesPath);
    if (buffer) {
      const src = buffer.toString();
      const insertion = new InsertChange(stylesPath, src.length, `\nbody { margin: 0; }\n`);
      const recorder = host.beginUpdate(stylesPath);
      recorder.insertLeft(insertion.pos, insertion.toAdd);
      host.commitUpdate(recorder);
    } else {
      console.warn(`Skipped body reset; could not find file: ${stylesPath}`);
    }
  };
Example #12
0
function getComponentTemplate(host: Tree, compPath: string, tmplInfo: TemplateInfo): string {
  let template = '';

  if (tmplInfo.templateProp) {
    template = tmplInfo.templateProp.getFullText();
  } else if (tmplInfo.templateUrlProp) {
    const templateUrl = (tmplInfo.templateUrlProp.initializer as ts.StringLiteral).text;
    const dir = dirname(normalize(compPath));
    const templatePath = join(dir, templateUrl);
    const buffer = host.read(templatePath);
    if (buffer) {
      template = buffer.toString();
    }
  }

  return template;
}
Example #13
0
File: index.ts Project: iwe7/devkit
  return (host: Tree, context: SchematicContext) => {
    const packageName = '@angular/service-worker';
    context.logger.debug(`adding dependency (${packageName})`);
    const buffer = host.read(packageJsonPath);
    if (buffer === null) {
      throw new SchematicsException('Could not find package.json');
    }

    const packageObject = JSON.parse(buffer.toString());

    const ngCoreVersion = packageObject.dependencies['@angular/core'];
    packageObject.dependencies[packageName] = ngCoreVersion;

    host.overwrite(packageJsonPath, JSON.stringify(packageObject, null, 2));

    return host;
  };
Example #14
0
  return (host: Tree) => {
    const packageJson = 'package.json';
    if (!host.exists(packageJson)) {
      throw new Error(`Could not find ${packageJson}`);
    }
    const packageJsonContent = host.read(packageJson);
    if (!packageJsonContent) {
      throw new Error('Failed to read package.json content');
    }
    const jsonAst = parseJsonAst(packageJsonContent.toString()) as JsonAstObject;
    const deps = findPropertyInAstObject(jsonAst, 'dependencies') as JsonAstObject;
    const devDeps = findPropertyInAstObject(jsonAst, 'devDependencies') as JsonAstObject;

    const angularCoreNode = findPropertyInAstObject(deps, '@angular/core');
    if (!angularCoreNode) {
      throw new Error('@angular/core dependency not found in package.json');
    }
    const angularCoreVersion = angularCoreNode.value as string;

    const devDependencies: {[k: string]: string} = {
      '@angular/bazel': angularCoreVersion,
      '@bazel/bazel': '^0.24.0',
      '@bazel/ibazel': '^0.10.1',
      '@bazel/karma': '0.27.12',
      '@bazel/typescript': '0.27.12',
    };

    const recorder = host.beginUpdate(packageJson);
    for (const packageName of Object.keys(devDependencies)) {
      const existingDep = findPropertyInAstObject(deps, packageName);
      if (existingDep) {
        const content = packageJsonContent.toString();
        removeKeyValueInAstObject(recorder, content, deps, packageName);
      }
      const version = devDependencies[packageName];
      const indent = 4;
      if (findPropertyInAstObject(devDeps, packageName)) {
        replacePropertyInAstObject(recorder, devDeps, packageName, version, indent);
      } else {
        insertPropertyInAstObjectInOrder(recorder, devDeps, packageName, version, indent);
      }
    }
    host.commitUpdate(recorder);
    return host;
  };
Example #15
0
File: index.ts Project: iwe7/devkit
  return (host: Tree, context: SchematicContext) => {
    const workspace = getWorkspace(host);
    const project = workspace.projects[options.project as string];
    let path: string;
    if (project && project.architect && project.architect.build &&
        project.architect.build.options.index) {
      path = project.architect.build.options.index;
    } else {
      throw new SchematicsException('Could not find index file for the project');
    }
    const buffer = host.read(path);
    if (buffer === null) {
      throw new SchematicsException(`Could not read index file: ${path}`);
    }
    const content = buffer.toString();
    const lines = content.split('\n');
    let closingHeadTagLineIndex = -1;
    let closingHeadTagLine = '';
    lines.forEach((line, index) => {
      if (/<\/head>/.test(line) && closingHeadTagLineIndex === -1) {
        closingHeadTagLine = line;
        closingHeadTagLineIndex = index;
      }
    });

    const indent = getIndent(closingHeadTagLine) + '  ';
    const itemsToAdd = [
      '<link rel="manifest" href="manifest.json">',
      '<meta name="theme-color" content="#1976d2">',
    ];

    const textToInsert = itemsToAdd
      .map(text => indent + text)
      .join('\n');

    const updatedIndex = [
      ...lines.slice(0, closingHeadTagLineIndex),
      textToInsert,
      ...lines.slice(closingHeadTagLineIndex),
    ].join('\n');

    host.overwrite(path, updatedIndex);

    return host;
  };
Example #16
0
function findBrowserModuleImport(host: Tree, modulePath: string): ts.Node {
  const moduleBuffer = host.read(modulePath);
  if (!moduleBuffer) {
    throw new SchematicsException(`Module file (${modulePath}) not found`);
  }
  const moduleFileText = moduleBuffer.toString('utf-8');

  const source = ts.createSourceFile(modulePath, moduleFileText, ts.ScriptTarget.Latest, true);

  const decoratorMetadata = getDecoratorMetadata(source, 'NgModule', '@angular/core')[0];
  const browserModuleNode = findNode(decoratorMetadata, ts.SyntaxKind.Identifier, 'BrowserModule');

  if (browserModuleNode === null) {
    throw new SchematicsException(`Cannot find BrowserModule import in ${modulePath}`);
  }

  return browserModuleNode;
}
Example #17
0
    return (host: Tree, context: SchematicContext) => {
        context.logger.info(`Applying migration for Ignite UI for Angular to version ${version}`);

        const update = new UpdateChanges(__dirname, host, context);
        // update.applyChanges();

        // rename submodule imports
        for (const entryPath of update.tsFiles) {
            let content = host.read(entryPath).toString();
            if (content.indexOf('igniteui-angular/') !== -1) {
                const pos = getImportModulePositions(content, 'igniteui-angular/');
                for (let i = pos.length; i--;) {
                    content = content.slice(0, pos[i].start) + 'igniteui-angular' + content.slice(pos[i].end);
                }
                host.overwrite(entryPath, content);
            }
        }
    };
Example #18
0
File: npm.ts Project: iwe7/devkit
    (tree: Tree, context: SchematicContext): Observable<Tree> => {
      const packageJsonContent = tree.read('/package.json');
      if (!packageJsonContent) {
        throw new SchematicsException('Could not find package.json.');
      }
      const packageJson = JSON.parse(packageJsonContent.toString());
      const packages: { [name: string]: string } = {};
      for (const name of supportedPackages) {
        packages[name] = version;
      }

      return concat(
        _getRecursiveVersions(packageJson, packages, allVersions, context.logger, loose).pipe(
          ignoreElements(),
        ),
        observableOf(tree),
      );
    },
Example #19
0
/**
 * Look for package.json file for @angular/core in node_modules and extract its
 * version.
 */
function findAngularVersion(options: BazelWorkspaceOptions, host: Tree): string|null {
  // Need to look in multiple locations because we could be working in a subtree.
  const candidates = [
    'node_modules/@angular/core/package.json',
    `${options.name}/node_modules/@angular/core/package.json`,
  ];
  for (const candidate of candidates) {
    if (host.exists(candidate)) {
      try {
        const packageJson = JSON.parse(host.read(candidate).toString());
        if (packageJson.name === '@angular/core' && packageJson.version) {
          return packageJson.version;
        }
      } catch {
      }
    }
  }
  return null;
}
Example #20
0
/**
 * Look for package.json file for package with `packageName` in node_modules and
 * extract its version.
 */
function findVersion(projectName: string, packageName: string, host: Tree): string|null {
  // Need to look in multiple locations because we could be working in a subtree.
  const candidates = [
    `node_modules/${packageName}/package.json`,
    `${projectName}/node_modules/${packageName}/package.json`,
  ];
  for (const candidate of candidates) {
    if (host.exists(candidate)) {
      try {
        const packageJson = JSON.parse(host.read(candidate).toString());
        if (packageJson.name === packageName && packageJson.version) {
          return packageJson.version;
        }
      } catch {
      }
    }
  }
  return null;
}
Example #21
0
 return (tree: Tree, context: SchematicContext) => {
   if (options.polyfills) {
     const workspace = getWorkspace(tree);
     const targetProperty = 'es5BrowserSupport';
     const polyfillsFile = 'src/polyfills.ts';
     const propertyExists = propertyExistsInWorkspace(targetProperty, workspace);
     let polyfillsData = tree.read(polyfillsFile).toString();
     if (propertyExists) {
       // If project targets angular cli version >= 7.3
       workspace.projects[workspace.defaultProject].architect.build.options[targetProperty] = true;
       enableWebAnimationsAndGridSupport(tree, polyfillsFile, polyfillsData);
       overwriteJsonFile(tree, 'angular.json', workspace);
     } else {
       // If project targets angular cli version < 7.3
       polyfillsData = enablePolyfills(tree, context);
       enableWebAnimationsAndGridSupport(tree, polyfillsFile, polyfillsData);
     }
   }
 };
Example #22
0
function enablePolyfills(tree: Tree, context: SchematicContext): string {
  const targetFile = 'src/polyfills.ts';
  if (!tree.exists(targetFile)) {
    context.logger.warn(`${targetFile} not found. You may need to update polyfills.ts manually.`);
    return;
  }

  // Match all commented import statements that are core-js/es6/*
  const pattern = /\/{2}\s{0,}(import\s{0,}\'core\-js\/es6\/.+)/;
  let polyfillsData = tree.read(targetFile).toString();
  if (pattern.test(polyfillsData)) {
    let result: any;
    while (result = pattern.exec(polyfillsData)) {
      polyfillsData = polyfillsData.replace(result[0], result[1]);
    }
  }

  return polyfillsData;
}
Example #23
0
File: index.ts Project: iwe7/devkit
function _getAllDependencies(tree: Tree): Map<string, VersionRange> {
  const packageJsonContent = tree.read('/package.json');
  if (!packageJsonContent) {
    throw new SchematicsException('Could not find a package.json. Are you in a Node project?');
  }

  let packageJson: JsonSchemaForNpmPackageJsonFiles;
  try {
    packageJson = JSON.parse(packageJsonContent.toString()) as JsonSchemaForNpmPackageJsonFiles;
  } catch (e) {
    throw new SchematicsException('package.json could not be parsed: ' + e.message);
  }

  return new Map<string, VersionRange>([
    ...Object.entries(packageJson.peerDependencies || {}),
    ...Object.entries(packageJson.devDependencies || {}),
    ...Object.entries(packageJson.dependencies || {}),
  ] as [string, VersionRange][]);
}
  return (host: Tree) => {
    const tsConfigPath = '/tsconfig.json';
    const buffer = host.read(tsConfigPath);
    if (!buffer) {
      return host;
    }

    const tsCfgAst = parseJsonAst(buffer.toString(), JsonParseMode.Loose);

    if (tsCfgAst.kind !== 'object') {
      return host;
    }

    const compilerOptions = findPropertyInAstObject(tsCfgAst, 'compilerOptions');
    if (!compilerOptions || compilerOptions.kind !== 'object') {
      return host;
    }

    const recorder = host.beginUpdate(tsConfigPath);

    const scriptTarget = findPropertyInAstObject(compilerOptions, 'target');
    if (!scriptTarget) {
      insertPropertyInAstObjectInOrder(recorder, compilerOptions, 'target', 'es2015', 4);
    } else if (scriptTarget.value !== 'es2015') {
      const { start, end } = scriptTarget;
      recorder.remove(start.offset, end.offset - start.offset);
      recorder.insertLeft(start.offset, '"es2015"');
    }

    const scriptModule = findPropertyInAstObject(compilerOptions, 'module');
    if (!scriptModule) {
      insertPropertyInAstObjectInOrder(recorder, compilerOptions, 'module', 'esnext', 4);
    } else if (scriptModule.value !== 'esnext') {
      const { start, end } = scriptModule;
      recorder.remove(start.offset, end.offset - start.offset);
      recorder.insertLeft(start.offset, '"esnext"');
    }

    host.commitUpdate(recorder);

    return updateBrowserlist;
  };
Example #25
0
export function addPackageToPackageJson(host: Tree, pkg: string, version: string): Tree {

  if (host.exists('package.json')) {
    const sourceText = host.read('package.json')!.toString('utf-8');
    const json = JSON.parse(sourceText);

    if (!json.dependencies) {
      json.dependencies = {};
    }

    if (!json.dependencies[pkg]) {
      json.dependencies[pkg] = version;
      json.dependencies = sortObjectByKeys(json.dependencies);
    }

    host.overwrite('package.json', JSON.stringify(json, null, 2));
  }

  return host;
}
Example #26
0
function getWorkspace(
  host: Tree,
): { path: string, workspace: experimental.workspace.WorkspaceSchema } {
  const possibleFiles = [ '/angular.json', '/.angular.json' ];
  const path = possibleFiles.filter(path => host.exists(path))[0];

  const configBuffer = host.read(path);
  if (configBuffer === null) {
    throw new SchematicsException(`Could not find (${path})`);
  }
  const content = configBuffer.toString();

  return {
    path,
    workspace: parseJson(
      content,
      JsonParseMode.Loose,
    ) as {} as experimental.workspace.WorkspaceSchema,
  };
}
Example #27
0
    (tree: Tree, context: SchematicContext): Observable<Tree> => {
      const packageJsonContent = tree.read('/package.json');
      if (!packageJsonContent) {
        throw new SchematicsException('Could not find package.json.');
      }
      const packageJson = parseJson(packageJsonContent.toString(), JsonParseMode.Strict);
      if (packageJson === null || typeof packageJson !== 'object' || Array.isArray(packageJson)) {
        throw new SchematicsException('Could not parse package.json.');
      }
      const packages: { [name: string]: string } = {};
      for (const name of supportedPackages) {
        packages[name] = version;
      }

      return concat(
        _getRecursiveVersions(packageJson, packages, allVersions, context.logger, loose).pipe(
          ignoreElements(),
        ),
        observableOf(tree),
      );
    },
Example #28
0
  return (host: Tree) => {
    const gitignore = `${options.name}/.gitignore`;
    if (!host.exists(gitignore)) {
      return host;
    }
    const gitIgnoreContent = host.read(gitignore);
    if (!gitIgnoreContent) {
      throw new Error('Failed to read .gitignore content');
    }

    if (gitIgnoreContent.includes('/bazel-out\n')) {
      return host;
    }
    const lines = gitIgnoreContent.toString().split(/\n/g);
    const recorder = host.beginUpdate(gitignore);
    const compileOutput = lines.findIndex((line: string) => line === '# compiled output');
    recorder.insertRight(compileOutput, '\n/bazel-out');
    host.commitUpdate(recorder);

    return host;
  };
export function hasNgModuleImport(tree: Tree, modulePath: string, className: string): boolean {
  const moduleFileContent = tree.read(modulePath);

  if (!moduleFileContent) {
    throw new Error(`Could not read Angular module file: ${modulePath}`);
  }

  const parsedFile = ts.createSourceFile(modulePath, moduleFileContent.toString(),
      ts.ScriptTarget.Latest, true);
  let ngModuleMetadata: ts.ObjectLiteralExpression | null = null;

  const findModuleDecorator = (node: ts.Node) => {
    if (ts.isDecorator(node) && ts.isCallExpression(node.expression) &&
        isNgModuleCallExpression(node.expression)) {
      ngModuleMetadata = node.expression.arguments[0] as ts.ObjectLiteralExpression;
      return;
    }

    ts.forEachChild(node, findModuleDecorator);
  };

  ts.forEachChild(parsedFile, findModuleDecorator);

  if (!ngModuleMetadata) {
    throw new Error(`Could not find NgModule declaration inside: "${modulePath}"`);
  }

  for (let property of ngModuleMetadata!.properties) {
    if (!ts.isPropertyAssignment(property) || property.name.getText() !== 'imports' ||
        !ts.isArrayLiteralExpression(property.initializer)) {
      continue;
    }

    if (property.initializer.elements.some(element => element.getText() === className)) {
      return true;
    }
  }

  return false;
}
Example #30
0
  return (host: Tree, context: SchematicContext) => {
    context.logger.debug('Updating appmodule');

    if (options.path === undefined) {
      return;
    }

    const siblingModules = host.getDir(options.path).subfiles
      // Find all files that start with the same name, are ts files, and aren't spec files.
      .filter(f => f.startsWith(options.name) && f.endsWith('.ts') && !f.endsWith('spec.ts'))
      // Sort alphabetically for consistency.
      .sort();

    if (siblingModules.length === 0) {
      // No module to add in.
      return;
    }

    const siblingModulePath = `${options.path}/${siblingModules[0]}`;
    const logMessage = 'console.log(`page got message: ${data}`);';
    const workerCreationSnippet = tags.stripIndent`
      if (typeof Worker !== 'undefined') {
        // Create a new
        const worker = new Worker('./${options.name}.worker', { type: 'module' });
        worker.onmessage = ({ data }) => {
          ${logMessage}
        };
        worker.postMessage('hello');
      } else {
        // Web Workers are not supported in this environment.
        // You should add a fallback so that your program still executes correctly.
      }
    `;

    // Append the worker creation snippet.
    const originalContent = host.read(siblingModulePath);
    host.overwrite(siblingModulePath, originalContent + '\n' + workerCreationSnippet);

    return host;
  };