Skip to content

Commit 0857121

Browse files
committed
react template
1 parent 0f06446 commit 0857121

File tree

14 files changed

+908
-818
lines changed

14 files changed

+908
-818
lines changed

src/commands/init.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ export async function initAction(feature: string, options: Options): Promise<voi
252252
if (setup.features.includes("hosting") && setup.features.includes("hosting:github")) {
253253
setup.features = setup.features.filter((f) => f !== "hosting:github");
254254
}
255+
// "dataconnect:sdk" is a part of "dataconnect", so if both are selected, "dataconnect:sdk" is ignored.
256+
if (setup.features.includes("dataconnect") && setup.features.includes("dataconnect:sdk")) {
257+
setup.features = setup.features.filter((f) => f !== "dataconnect:sdk");
258+
}
255259

256260
await init(setup, config, options);
257261

src/dataconnect/appFinder.spec.ts

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import * as mockfs from "mock-fs";
2+
3+
import { expect } from "chai";
4+
import { getPlatformFromFolder } from "./appFinder";
5+
import { Platform } from "./types";
6+
import FileSystem from "mock-fs/lib/filesystem";
7+
8+
describe("getPlatformFromFolder", () => {
9+
const cases: {
10+
desc: string;
11+
folderName: string;
12+
folderItems: FileSystem.DirectoryItems;
13+
output: Platform;
14+
}[] = [
15+
{
16+
desc: "Empty folder",
17+
folderName: "test/",
18+
folderItems: {},
19+
output: Platform.NONE,
20+
},
21+
{
22+
desc: "Empty folder, long path name",
23+
folderName: "root/abcd/test/",
24+
folderItems: {},
25+
output: Platform.NONE,
26+
},
27+
{
28+
desc: "folder w/ no identifier",
29+
folderName: "test/",
30+
folderItems: { file1: "contents", randomfile2: "my android contents" },
31+
output: Platform.NONE,
32+
},
33+
{
34+
desc: "Web identifier 1",
35+
folderName: "test/",
36+
folderItems: { file1: "contents", "package.json": "node" },
37+
output: Platform.WEB,
38+
},
39+
{
40+
desc: "Web identifier 2",
41+
folderName: "test/",
42+
folderItems: { file1: "contents", node_modules: { dep1: "firebase", dep2: "dataconnect" } },
43+
output: Platform.WEB,
44+
},
45+
{
46+
desc: "Android identifier 1",
47+
folderName: "/test",
48+
folderItems: { file1: "contents", "androidmanifest.xml": "my android contents" },
49+
output: Platform.ANDROID,
50+
},
51+
{
52+
desc: "Android identifier 2",
53+
folderName: "/test/",
54+
folderItems: {
55+
"build.gradle": "contents",
56+
file2: "my android contents",
57+
},
58+
output: Platform.ANDROID,
59+
},
60+
{
61+
desc: "Android identifier, long path name",
62+
folderName: "is/this/an/android/test",
63+
folderItems: { file1: "contents", "androidmanifest.xml": "my android contents" },
64+
output: Platform.ANDROID,
65+
},
66+
{
67+
desc: "iOS file identifier 1",
68+
folderName: "test/",
69+
folderItems: { file1: "contents", podfile: "cocoa pods yummy" },
70+
output: Platform.IOS,
71+
},
72+
{
73+
desc: "iOS file identifier 2",
74+
folderName: "root/abcd",
75+
folderItems: {
76+
file1: "ios",
77+
"myapp.xcodeproj": "folder in an xcode folder",
78+
},
79+
output: Platform.IOS,
80+
},
81+
{
82+
desc: "iOS folder identifier 3",
83+
folderName: "/users/googler/myprojects",
84+
folderItems: {
85+
"myapp.xcworkspace": { file1: "contents" },
86+
},
87+
output: Platform.IOS,
88+
},
89+
{
90+
desc: "Flutter identifier 1",
91+
folderName: "is/this/a/dart/test",
92+
folderItems: {
93+
file1: "contents",
94+
"pubspec.yaml": "my deps",
95+
},
96+
output: Platform.FLUTTER,
97+
},
98+
{
99+
desc: "Flutter identifier 2",
100+
folderName: "is/this/a/dart/test",
101+
folderItems: {
102+
"pubspec.lock": "my deps",
103+
},
104+
output: Platform.FLUTTER,
105+
},
106+
{
107+
desc: "Flutter identifier with experiment disabled",
108+
folderName: "is/this/a/dart/test",
109+
folderItems: {
110+
"pubspec.mispelled": "my deps",
111+
},
112+
output: Platform.NONE,
113+
},
114+
{
115+
desc: "multiple identifiers, returns undetermined",
116+
folderName: "test/",
117+
folderItems: {
118+
file1: "contents",
119+
podfile: "cocoa pods yummy",
120+
"androidmanifest.xml": "file found second :(",
121+
},
122+
output: Platform.MULTIPLE,
123+
},
124+
];
125+
for (const c of cases) {
126+
it(c.desc, async () => {
127+
mockfs({ [c.folderName]: c.folderItems });
128+
const platform = await getPlatformFromFolder(c.folderName);
129+
expect(platform).to.equal(c.output);
130+
});
131+
}
132+
afterEach(() => {
133+
mockfs.restore();
134+
});
135+
});

src/dataconnect/appFinder.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import * as fs from "fs-extra";
2+
import * as path from "path";
3+
import { glob } from "glob";
4+
import { Framework, Platform } from "./types";
5+
import { PackageJSON } from "../frameworks/compose/discover/runtime/node";
6+
7+
export interface App {
8+
platform: Platform;
9+
directory: string;
10+
frameworks?: Framework[];
11+
}
12+
13+
export function appDescription(a: App): string {
14+
if (a.directory === ".") {
15+
return `. (${a.platform.toLowerCase()})`;
16+
}
17+
return `${a.directory} (${a.platform.toLowerCase()})`;
18+
}
19+
20+
/** Given a directory, determine the platform type */
21+
export async function getPlatformFromFolder(dirPath: string): Promise<Platform> {
22+
const apps = await detectApps(dirPath);
23+
const hasWeb = apps.some((app) => app.platform === Platform.WEB);
24+
const hasAndroid = apps.some((app) => app.platform === Platform.ANDROID);
25+
const hasIOS = apps.some((app) => app.platform === Platform.IOS);
26+
const hasDart = apps.some((app) => app.platform === Platform.FLUTTER);
27+
if (!hasWeb && !hasAndroid && !hasIOS && !hasDart) {
28+
return Platform.NONE;
29+
} else if (hasWeb && !hasAndroid && !hasIOS && !hasDart) {
30+
return Platform.WEB;
31+
} else if (hasAndroid && !hasWeb && !hasIOS && !hasDart) {
32+
return Platform.ANDROID;
33+
} else if (hasIOS && !hasWeb && !hasAndroid && !hasDart) {
34+
return Platform.IOS;
35+
} else if (hasDart && !hasWeb && !hasIOS && !hasAndroid) {
36+
return Platform.FLUTTER;
37+
}
38+
// At this point, its not clear which platform the app directory is
39+
// because we found indicators for multiple platforms.
40+
return Platform.MULTIPLE;
41+
}
42+
43+
/** Detects the apps in a given directory */
44+
export async function detectApps(dirPath: string): Promise<App[]> {
45+
const packageJsonFiles = await detectFiles(dirPath, "package.json");
46+
const pubSpecYamlFiles = await detectFiles(dirPath, "pubspec.yaml");
47+
const srcMainFolders = await detectFiles(dirPath, "src/main/");
48+
const xCodeProjects = await detectFiles(dirPath, "*.xcodeproj/");
49+
const apps: App[] = [
50+
...(await Promise.all(packageJsonFiles.map(packageJsonToWebApp))),
51+
...pubSpecYamlFiles.map((f) => ({ platform: Platform.FLUTTER, directory: path.dirname(f) })),
52+
...srcMainFolders.map((f) => ({
53+
platform: Platform.ANDROID,
54+
directory: path.dirname(path.dirname(f)),
55+
})),
56+
...xCodeProjects.map((f) => ({ platform: Platform.IOS, directory: path.dirname(f) })),
57+
];
58+
return apps;
59+
}
60+
61+
async function packageJsonToWebApp(packageJsonFile: string): Promise<App> {
62+
const packageJson = JSON.parse((await fs.readFile(packageJsonFile)).toString());
63+
return {
64+
platform: Platform.WEB,
65+
directory: path.dirname(packageJsonFile),
66+
frameworks: getFrameworksFromPackageJson(packageJson),
67+
};
68+
}
69+
70+
export const WEB_FRAMEWORKS: Framework[] = ["react", "angular"];
71+
export const WEB_FRAMEWORKS_SIGNALS: { [key in Framework]: string[] } = {
72+
react: ["react", "next"],
73+
angular: ["@angular/core"],
74+
};
75+
76+
export function getFrameworksFromPackageJson(packageJson: PackageJSON): Framework[] {
77+
const devDependencies = Object.keys(packageJson.devDependencies ?? {});
78+
const dependencies = Object.keys(packageJson.dependencies ?? {});
79+
const allDeps = Array.from(new Set([...devDependencies, ...dependencies]));
80+
return WEB_FRAMEWORKS.filter((framework) =>
81+
WEB_FRAMEWORKS_SIGNALS[framework]!.find((dep) => allDeps.includes(dep)),
82+
);
83+
}
84+
85+
async function detectFiles(dirPath: string, filePattern: string): Promise<string[]> {
86+
const options = {
87+
cwd: dirPath,
88+
ignore: [
89+
"**/dataconnect*/**",
90+
"**/node_modules/**", // Standard dependency directory
91+
"**/dist/**", // Common build output
92+
"**/build/**", // Common build output
93+
"**/out/**", // Another common build output
94+
"**/.next/**", // Next.js build directory
95+
"**/coverage/**", // Test coverage reports
96+
],
97+
absolute: false,
98+
};
99+
return glob(`{${filePattern},*/${filePattern},*/*/${filePattern}}`, options);
100+
}

0 commit comments

Comments
 (0)