18d307f52SEvan Baconimport { IOSConfig } from '@expo/config-plugins';
28d307f52SEvan Baconimport Minipass from 'minipass';
38d307f52SEvan Baconimport path from 'path';
48d307f52SEvan Baconimport { ReadEntry } from 'tar';
58d307f52SEvan Bacon
68d307f52SEvan Baconfunction escapeXMLCharacters(original: string): string {
78d307f52SEvan Bacon  const noAmps = original.replace('&', '&');
88d307f52SEvan Bacon  const noLt = noAmps.replace('<', '&lt;');
98d307f52SEvan Bacon  const noGt = noLt.replace('>', '&gt;');
108d307f52SEvan Bacon  const noApos = noGt.replace('"', '\\"');
118d307f52SEvan Bacon  return noApos.replace("'", "\\'");
128d307f52SEvan Bacon}
138d307f52SEvan Bacon
148d307f52SEvan Baconclass Transformer extends Minipass {
158d307f52SEvan Bacon  data = '';
168d307f52SEvan Bacon
178d307f52SEvan Bacon  constructor(private settings: { name: string; extension: string }) {
188d307f52SEvan Bacon    super();
198d307f52SEvan Bacon  }
208d307f52SEvan Bacon
218d307f52SEvan Bacon  write(data: string) {
228d307f52SEvan Bacon    this.data += data;
238d307f52SEvan Bacon    return true;
248d307f52SEvan Bacon  }
258d307f52SEvan Bacon
268d307f52SEvan Bacon  getNormalizedName(): string {
278d307f52SEvan Bacon    if (['.xml', '.plist'].includes(this.settings.extension)) {
288d307f52SEvan Bacon      return escapeXMLCharacters(this.settings.name);
298d307f52SEvan Bacon    }
308d307f52SEvan Bacon    return this.settings.name;
318d307f52SEvan Bacon  }
328d307f52SEvan Bacon
338d307f52SEvan Bacon  end() {
348d307f52SEvan Bacon    const name = this.getNormalizedName();
358d307f52SEvan Bacon    const replaced = this.data
368d307f52SEvan Bacon      .replace(/Hello App Display Name/g, name)
378d307f52SEvan Bacon      .replace(/HelloWorld/g, IOSConfig.XcodeUtils.sanitizedName(name))
388d307f52SEvan Bacon      .replace(/helloworld/g, IOSConfig.XcodeUtils.sanitizedName(name.toLowerCase()));
398d307f52SEvan Bacon    super.write(replaced);
408d307f52SEvan Bacon    return super.end();
418d307f52SEvan Bacon  }
428d307f52SEvan Bacon}
438d307f52SEvan Bacon
448d307f52SEvan Baconexport function createEntryResolver(name: string) {
458d307f52SEvan Bacon  return (entry: ReadEntry) => {
468d307f52SEvan Bacon    if (name) {
478d307f52SEvan Bacon      // Rewrite paths for bare workflow
488d307f52SEvan Bacon      entry.path = entry.path
498d307f52SEvan Bacon        .replace(
508d307f52SEvan Bacon          /HelloWorld/g,
518d307f52SEvan Bacon          entry.path.includes('android')
528d307f52SEvan Bacon            ? IOSConfig.XcodeUtils.sanitizedName(name.toLowerCase())
538d307f52SEvan Bacon            : IOSConfig.XcodeUtils.sanitizedName(name)
548d307f52SEvan Bacon        )
558d307f52SEvan Bacon        .replace(/helloworld/g, IOSConfig.XcodeUtils.sanitizedName(name).toLowerCase());
568d307f52SEvan Bacon    }
578d307f52SEvan Bacon    if (entry.type && /^file$/i.test(entry.type) && path.basename(entry.path) === 'gitignore') {
588d307f52SEvan Bacon      // Rename `gitignore` because npm ignores files named `.gitignore` when publishing.
598d307f52SEvan Bacon      // See: https://github.com/npm/npm/issues/1862
608d307f52SEvan Bacon      entry.path = entry.path.replace(/gitignore$/, '.gitignore');
618d307f52SEvan Bacon    }
628d307f52SEvan Bacon  };
638d307f52SEvan Bacon}
648d307f52SEvan Bacon
658d307f52SEvan Baconexport function createFileTransform(name: string) {
668d307f52SEvan Bacon  return (entry: ReadEntry) => {
678d307f52SEvan Bacon    const extension = path.extname(entry.path);
688d307f52SEvan Bacon
698d307f52SEvan Bacon    // Binary files, don't process these (avoid decoding as utf8)
708d307f52SEvan Bacon    if (
718d307f52SEvan Bacon      ![
728d307f52SEvan Bacon        '.png',
738d307f52SEvan Bacon        '.jpg',
748d307f52SEvan Bacon        '.jpeg',
758d307f52SEvan Bacon        '.gif',
768d307f52SEvan Bacon        '.webp',
778d307f52SEvan Bacon        '.psd',
788d307f52SEvan Bacon        '.tiff',
798d307f52SEvan Bacon        '.svg',
808d307f52SEvan Bacon        '.jar',
818d307f52SEvan Bacon        '.keystore',
82*f3dc3b4eSEvan Bacon
83*f3dc3b4eSEvan Bacon        // Font files
84*f3dc3b4eSEvan Bacon        '.otf',
85*f3dc3b4eSEvan Bacon        '.ttf',
868d307f52SEvan Bacon      ].includes(extension) &&
878d307f52SEvan Bacon      name
888d307f52SEvan Bacon    ) {
898d307f52SEvan Bacon      return new Transformer({ name, extension });
908d307f52SEvan Bacon    }
918d307f52SEvan Bacon    return undefined;
928d307f52SEvan Bacon  };
938d307f52SEvan Bacon}
94