1import { IOSConfig } from '@expo/config-plugins';
2import Minipass from 'minipass';
3import path from 'path';
4import { ReadEntry } from 'tar';
5
6function escapeXMLCharacters(original: string): string {
7  const noAmps = original.replace('&', '&');
8  const noLt = noAmps.replace('<', '&lt;');
9  const noGt = noLt.replace('>', '&gt;');
10  const noApos = noGt.replace('"', '\\"');
11  return noApos.replace("'", "\\'");
12}
13
14class Transformer extends Minipass {
15  data = '';
16
17  constructor(private settings: { name: string; extension: string }) {
18    super();
19  }
20
21  write(data: string) {
22    this.data += data;
23    return true;
24  }
25
26  getNormalizedName(): string {
27    if (['.xml', '.plist'].includes(this.settings.extension)) {
28      return escapeXMLCharacters(this.settings.name);
29    }
30    return this.settings.name;
31  }
32
33  end() {
34    const name = this.getNormalizedName();
35    const replaced = this.data
36      .replace(/Hello App Display Name/g, name)
37      .replace(/HelloWorld/g, IOSConfig.XcodeUtils.sanitizedName(name))
38      .replace(/helloworld/g, IOSConfig.XcodeUtils.sanitizedName(name.toLowerCase()));
39    super.write(replaced);
40    return super.end();
41  }
42}
43
44export function createEntryResolver(name: string) {
45  return (entry: ReadEntry) => {
46    if (name) {
47      // Rewrite paths for bare workflow
48      entry.path = entry.path
49        .replace(
50          /HelloWorld/g,
51          entry.path.includes('android')
52            ? IOSConfig.XcodeUtils.sanitizedName(name.toLowerCase())
53            : IOSConfig.XcodeUtils.sanitizedName(name)
54        )
55        .replace(/helloworld/g, IOSConfig.XcodeUtils.sanitizedName(name).toLowerCase());
56    }
57    if (entry.type && /^file$/i.test(entry.type) && path.basename(entry.path) === 'gitignore') {
58      // Rename `gitignore` because npm ignores files named `.gitignore` when publishing.
59      // See: https://github.com/npm/npm/issues/1862
60      entry.path = entry.path.replace(/gitignore$/, '.gitignore');
61    }
62  };
63}
64
65export function createFileTransform(name: string) {
66  return (entry: ReadEntry) => {
67    const extension = path.extname(entry.path);
68
69    // Binary files, don't process these (avoid decoding as utf8)
70    if (
71      ![
72        '.png',
73        '.jpg',
74        '.jpeg',
75        '.gif',
76        '.webp',
77        '.psd',
78        '.tiff',
79        '.svg',
80        '.jar',
81        '.keystore',
82      ].includes(extension) &&
83      name
84    ) {
85      return new Transformer({ name, extension });
86    }
87    return undefined;
88  };
89}
90