Add capacitorjs runtime

This commit is contained in:
olcxja 2026-05-03 17:09:55 +02:00
commit f90c0e6c40
8362 changed files with 1502407 additions and 1 deletions

View file

@ -0,0 +1,90 @@
import { VFS, VFSStorable } from '../vfs';
import { AndroidGradleInjectType } from '../definitions';
export type GradleAST = any;
export interface GradleASTNode {
type: string;
name: string;
children?: GradleASTNode[];
source: {
line: number;
column: number;
lastLine: number;
lastColumn: number;
};
}
export declare class GradleFile extends VFSStorable {
filename: string;
private vfs;
private source;
private parsed;
private tempFile;
constructor(filename: string, vfs: VFS);
getDocument(): string | null;
/**
* Replace the given properties at the specified point in the Gradle file or insert
* if the replacement doesn't exist
*
* exact specifies whether the pathObject should be exact from the root of the document or
* if it can match on a sub-object
**/
replaceProperties(pathObject: any, toReplace: any, exact?: boolean): Promise<void>;
_makeReplacePathObject(pathObject: any, injectKey: string): any;
/**
* Replace an entry in the gradle file.
*/
private replaceInGradleFile;
/**
* Insert the given properties at the specified point in the Gradle file.
* exact specifies whether the pathObject should be exact from the root of the document or
* if it can match on a sub-object
**/
insertProperties(pathObject: any, toInject: any[], type?: AndroidGradleInjectType, exact?: boolean): Promise<void>;
/**
* Inject the given properties at the specified point in the Gradle file.
* exact specifies whether the pathObject should be exact from the root of the document or
* if it can match on a sub-object
**/
insertFragment(pathObject: any, toInject: string, exact?: boolean): Promise<void>;
/**
* Parse the underlying Gradle file and build the AST. Note: this calls out to
* a Java process which incurs some overhead and requires java to be installed
* This is because Gradle is actually a DSL for the Groovy language, which is
* a JVM language. Additionally, the Groovy parser is based on a modified version
* of the Antlr project that is tightly bound to the JVM. Ultimatley, this means
* the only safe, accurate way to feasibly build a Gradle AST is to use the Groovy
* parser API which this uses under the hood.
*/
parse(): Promise<any>;
/**
* Inject a modification into the gradle file.
*/
private insertIntoGradleFile;
find(pathObject: any | null, exact?: boolean): {
node: GradleASTNode;
depth: number;
}[];
private _find;
getSource(node: GradleASTNode): string;
private getDepth;
private matchesExact;
private isTargetNode;
getJava(): Promise<string | null>;
getGradleParserPath(): string;
setApplicationId(applicationId: string): Promise<void>;
getApplicationId(): Promise<string | null>;
setVersionCode(versionCode: number): Promise<void>;
getVersionCode(): Promise<number | null>;
incrementVersionCode(): Promise<void>;
setVersionName(versionName: string): Promise<void>;
getVersionName(): Promise<string | null>;
setVersionNameSuffix(versionNameSuffix: string): Promise<void>;
getVersionNameSuffix(): Promise<string | null>;
getNamespace(): Promise<string | null>;
setNamespace(namespace: string): Promise<void>;
private createGradleSource;
private getGradleSource;
private gradleParseError;
private gradleCommitFn;
private gradleDiffFn;
}
//# sourceMappingURL=gradle-file.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"gradle-file.d.ts","sourceRoot":"","sources":["../../src/android/gradle-file.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,GAAG,EAAW,WAAW,EAAW,MAAM,QAAQ,CAAC;AAE5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAIzD,MAAM,MAAM,SAAS,GAAG,GAAG,CAAC;AAC5B,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,qBAAa,UAAW,SAAQ,WAAW;IAKtB,QAAQ,EAAE,MAAM;IAAE,OAAO,CAAC,GAAG;IAJhD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,QAAQ,CAAuB;gBAEpB,QAAQ,EAAE,MAAM,EAAU,GAAG,EAAE,GAAG;IAIrD,WAAW;IAIX;;;;;;QAMI;IACE,iBAAiB,CAAC,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAkCtF,sBAAsB,CAAC,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM;IAsBzD;;OAEG;YAIW,mBAAmB;IAsDjC;;;;QAII;IACE,gBAAgB,CAAC,UAAU,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,IAAI,GAAE,uBAAwD,EAAE,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB/J;;;;QAII;IACE,cAAc,CAAC,UAAU,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBrF;;;;;;;;OAQG;IACG,KAAK;IAyEX;;OAEG;YAIW,oBAAoB;IAuFlC,IAAI,CAAC,UAAU,EAAE,GAAG,GAAG,IAAI,EAAE,KAAK,UAAQ,GAAG;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE;IAmBrF,OAAO,CAAC,KAAK;IA4Cb,SAAS,CAAC,IAAI,EAAE,aAAa;IAqB7B,OAAO,CAAC,QAAQ;IAiBhB,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,YAAY;IAId,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAkBvC,mBAAmB;IAIb,gBAAgB,CAAC,aAAa,EAAE,MAAM;IAWtC,gBAAgB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAe1C,cAAc,CAAC,WAAW,EAAE,MAAM;IAiBlC,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAaxC,oBAAoB;IAgBpB,cAAc,CAAC,WAAW,EAAE,MAAM;IAiBlC,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAexC,oBAAoB,CAAC,iBAAiB,EAAE,MAAM;IAkB9C,oBAAoB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAe9C,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAetC,YAAY,CAAC,SAAS,EAAE,MAAM;IA+BpC,OAAO,CAAC,kBAAkB;YAyEZ,eAAe;IAW7B,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,cAAc,CAMpB;IAEF,OAAO,CAAC,YAAY,CAUlB;CACH"}

View file

@ -0,0 +1,629 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GradleFile = void 0;
const path_1 = require("path");
const os_1 = __importDefault(require("os"));
const tempy_1 = __importDefault(require("tempy"));
const utils_fs_1 = require("@ionic/utils-fs");
const subprocess_1 = require("../util/subprocess");
const text_1 = require("../util/text");
const vfs_1 = require("../vfs");
const detect_indent_1 = __importDefault(require("../util/detect-indent"));
const logger_1 = require("../logger");
const fs_1 = require("../util/fs");
class GradleFile extends vfs_1.VFSStorable {
constructor(filename, vfs) {
super();
this.filename = filename;
this.vfs = vfs;
this.source = null;
this.parsed = null;
this.tempFile = null;
this.gradleCommitFn = async (file) => {
await (0, fs_1.assertParentDirs)(file.getFilename());
return (0, utils_fs_1.writeFile)(file.getFilename(), file.getData().getDocument());
};
this.gradleDiffFn = async (file) => {
var _a;
let old = '';
try {
old = await (0, utils_fs_1.readFile)(file.getFilename(), { encoding: 'utf-8' });
}
catch (e) { }
return {
old,
new: (_a = this.source) !== null && _a !== void 0 ? _a : '',
};
};
}
getDocument() {
return this.source;
}
/**
* Replace the given properties at the specified point in the Gradle file or insert
* if the replacement doesn't exist
*
* exact specifies whether the pathObject should be exact from the root of the document or
* if it can match on a sub-object
**/
async replaceProperties(pathObject, toReplace, exact = false) {
await this.parse();
if (!this.parsed) {
throw new Error('Call parse() first to load Gradle file');
}
const found = this.find(pathObject, exact);
if (!found.length) {
// Create a parent selector object since we're going to insert instead
const parent = this._makeReplacePathObject(pathObject, Object.keys(toReplace)[0]);
const foundParent = this.find(parent, exact);
if (foundParent.length) {
this.insertIntoGradleFile([toReplace], foundParent[0], "infer" /* AndroidGradleInjectType.Infer */);
return;
}
else {
throw new Error('Unable to find target in Gradle file to replace or insert');
}
}
const target = found[0];
return this.replaceInGradleFile(toReplace, target);
}
// Build a new pathObject that is the path to the parent rather than
// the path in pathObject
_makeReplacePathObject(pathObject, injectKey) {
let x = {};
let y = x;
let a = pathObject;
while (a) {
const keys = Object.keys(a);
if (keys[0] === injectKey || !keys.length) {
return y;
}
const o = {};
x[keys[0]] = o;
x = o;
a = a[keys[0]];
}
return y;
}
/**
* Replace an entry in the gradle file.
*/
// This is a beast, sorry. Hey, at least there's tests
// In the future, this could be moved to the Java `gradle-parse` package provided in this monorepo
// along with modifying the AST to inject our script but this works fine for now
async replaceInGradleFile(toInject, targetNode) {
var _a;
// These values are 1-indexed not 0-indexed
//let { line, column, lastLine, lastColumn } = targetNode.node.source;
let { line, column, lastLine, lastColumn } = targetNode.node.source;
const source = (_a = (await this.getGradleSource())) !== null && _a !== void 0 ? _a : '';
const sourceLines = source.split(/\r?\n/);
if (line == -1) {
// Set to first line (remember, 1-indexed)
line = 1;
}
if (lastLine === -1) {
// Set to last line (remember, 1-indexed)
lastLine = sourceLines.length + 1;
}
const detectedIndent = (0, detect_indent_1.default)(source);
let lines = [];
this.createGradleSource([toInject], lines /* out */, detectedIndent.indent, undefined, targetNode.node, "infer" /* AndroidGradleInjectType.Infer */);
const resolvedLastLine = lastLine < 0 ? sourceLines.length : lastLine;
const formatted = lines.join('\n');
const indentAmount = targetNode.depth;
const indented = (0, text_1.indent)(formatted, detectedIndent.indent, indentAmount - 1);
// Replace the target lines with our new source line
const newSource = sourceLines.slice(0, Math.max(0, line - 1)).join('\n') +
'\n' +
indented +
'\n' +
sourceLines
.slice(Math.max(0, resolvedLastLine), sourceLines.length)
.join('\n');
this.source = newSource;
}
/**
* Insert the given properties at the specified point in the Gradle file.
* exact specifies whether the pathObject should be exact from the root of the document or
* if it can match on a sub-object
**/
async insertProperties(pathObject, toInject, type = "method" /* AndroidGradleInjectType.Method */, exact = false) {
await this.parse();
if (!this.parsed) {
throw new Error('Call parse() first to load Gradle file');
}
const found = this.find(pathObject, exact);
if (!found.length) {
throw new Error('Unable to find method in Gradle file to inject');
}
const target = found[0];
return this.insertIntoGradleFile(toInject, target, type);
}
/**
* Inject the given properties at the specified point in the Gradle file.
* exact specifies whether the pathObject should be exact from the root of the document or
* if it can match on a sub-object
**/
async insertFragment(pathObject, toInject, exact = false) {
await this.parse();
if (!this.parsed) {
throw new Error('Call parse() first to load Gradle file');
}
const found = this.find(pathObject, exact);
if (!found.length) {
throw new Error('Unable to find method in Gradle file to inject');
}
const target = found[0];
return this.insertIntoGradleFile(toInject, target, "infer" /* AndroidGradleInjectType.Infer */);
}
/**
* Parse the underlying Gradle file and build the AST. Note: this calls out to
* a Java process which incurs some overhead and requires java to be installed
* This is because Gradle is actually a DSL for the Groovy language, which is
* a JVM language. Additionally, the Groovy parser is based on a modified version
* of the Antlr project that is tightly bound to the JVM. Ultimatley, this means
* the only safe, accurate way to feasibly build a Gradle AST is to use the Groovy
* parser API which this uses under the hood.
*/
async parse() {
var _a, _b;
if (!(await (0, utils_fs_1.pathExists)(this.filename))) {
throw new Error(`Unable to locate file at ${this.filename}`);
}
const vfsRef = this.vfs.get(this.filename);
// We keep a temp file updated with the latest source so the parser can operate
// on the current state of the file so we can handle multiple modifications to it
// in sequence
if (!this.tempFile) {
// If the temp file doesn't exist yet, create it and write the current file source to it
const gradleContents = await this.getGradleSource();
this.tempFile = tempy_1.default.file({ extension: 'gradle' });
await (0, utils_fs_1.writeFile)(this.tempFile, gradleContents);
}
else if (vfsRef) {
// Otherwise if it already exists then write the current vfs data to it
if ((_a = vfsRef === null || vfsRef === void 0 ? void 0 : vfsRef.getData()) === null || _a === void 0 ? void 0 : _a.getDocument()) {
await (0, utils_fs_1.writeFile)(this.tempFile, (_b = vfsRef === null || vfsRef === void 0 ? void 0 : vfsRef.getData()) === null || _b === void 0 ? void 0 : _b.getDocument());
}
}
const parserRoot = this.getGradleParserPath();
const java = await this.getJava();
if (!java) {
throw new Error(this.gradleParseError());
}
logger_1.Logger.v('gradle', 'parse', `running Gradle parse with Java path ${java}`);
logger_1.Logger.v('gradle', 'parse', `read gradle file at ${this.filename}`);
try {
let json = null;
if (os_1.default.platform() === 'win32') {
json = await (0, subprocess_1.spawnCommand)(java, [
'-cp',
'lib/groovy-3.0.9.jar;lib/json-20210307.jar;capacitor-gradle-parse.jar;.',
'com.capacitorjs.gradle.Parse',
this.tempFile,
], {
cwd: parserRoot,
stdio: 'pipe',
});
}
else {
json = await (0, subprocess_1.spawnCommand)(java, [
'-cp',
'lib/*:capacitor-gradle-parse.jar:.',
'com.capacitorjs.gradle.Parse',
this.tempFile,
], {
cwd: parserRoot,
stdio: 'pipe',
});
}
this.parsed = JSON.parse(json || '{}');
return this.parsed;
}
catch (e) {
throw new Error(`Unable to load or parse gradle file: ${e}`);
}
}
/**
* Inject a modification into the gradle file.
*/
// This is a beast, sorry. Hey, at least there's tests
// In the future, this could be moved to the Java `gradle-parse` package provided in this monorepo
// along with modifying the AST to inject our script but this works fine forn ow
async insertIntoGradleFile(toInject, targetNode, type) {
var _a;
// These values are 1-indexed not 0-indexed
let { line, column, lastLine, lastColumn } = targetNode.node.source;
const source = (_a = (await this.getGradleSource())) !== null && _a !== void 0 ? _a : '';
const sourceLines = source.split(/\r?\n/);
if (line == -1) {
// Set to first line (remember, 1-indexed)
line = 1;
}
if (lastLine === -1) {
// Set to last line (remember, 1-indexed)
lastLine = sourceLines.length + 1;
}
const detectedIndent = (0, detect_indent_1.default)(source);
let lines = [];
if (Array.isArray(toInject)) {
this.createGradleSource(toInject, lines /* out */, detectedIndent.indent, undefined, targetNode.node, type);
}
else {
lines = toInject.split(/\r?\n/);
}
const resolvedLastLine = lastLine < 0 ? sourceLines.length : lastLine;
const formatted = lines.join('\n');
const indentAmount = targetNode.depth;
let newSource = null;
if (line === lastLine) {
// Block is empty, like dependencies {}
const indented = (0, text_1.indent)(formatted, detectedIndent.indent, indentAmount);
const sourceLine = sourceLines[line - 1];
// The new line is the slice from the start of the line to one character before the end (remember,
// the lines and columns are 1-indexed so lastColumn - 2 is one character before the end
const newLine = sourceLine.slice(0, Math.max(0, lastColumn - 2)) +
'\n' +
indented +
'\n' +
(0, text_1.indent)(sourceLine.slice(Math.max(0, lastColumn - 2)).trim(), detectedIndent.indent, Math.max(0, indentAmount - 1));
newSource =
sourceLines.slice(0, Math.max(0, resolvedLastLine - 1)).join('\n') +
'\n' +
newLine +
'\n' +
sourceLines
.slice(Math.max(0, resolvedLastLine), sourceLines.length)
.join('\n');
}
else {
const indented = (0, text_1.indent)(formatted, detectedIndent.indent, indentAmount);
newSource =
sourceLines.slice(0, Math.max(0, resolvedLastLine - 1)).join('\n') +
'\n' +
indented +
'\n' +
sourceLines
.slice(Math.max(0, resolvedLastLine - 1), sourceLines.length)
.join('\n');
}
this.source = newSource;
}
find(pathObject, exact = false) {
var _a;
if (!this.parsed) {
throw new Error('Call parse() first to load Gradle file');
}
// Null or empty object means the root node
if (!pathObject || !Object.keys(pathObject).length) {
const firstChild = (_a = this.parsed.children) === null || _a === void 0 ? void 0 : _a[0];
if (firstChild) {
return [{ node: firstChild, depth: 0 }];
}
return [];
}
const found = [];
this._find(pathObject, this.parsed, pathObject, exact, [], found);
return found;
}
_find(pathObject, node, pathNode, exact, pathToNode, found, depth = 0) {
var _a, _b;
if (!pathNode) {
return;
}
const targetKey = (_a = Object.keys(pathNode)) === null || _a === void 0 ? void 0 : _a[0];
if (!targetKey) {
return;
}
for (const c of ((_b = node.children) !== null && _b !== void 0 ? _b : [])) {
if (this.isTargetNode(c) && c.name === targetKey) {
pathNode = pathNode[targetKey];
if (!pathNode || Object.keys(pathNode).length == 0) {
// We've run out of path nodes to match
if (!exact) {
found.push({ node: c, depth });
}
else if (exact && this.matchesExact(pathObject, c, [...pathToNode, c])) {
found.push({ node: c, depth });
}
}
}
const newPathToNode = this.isTargetNode(node) ? [...pathToNode, node] : pathToNode;
this._find(pathObject, c, pathNode, exact, newPathToNode, found, c.type === 'block' ? depth + 1 : depth);
}
}
getSource(node) {
if (!this.parsed || !this.source) {
throw new Error('Call parse() first to load Gradle file');
}
const lines = this.source.split(/\r?\n/);
const sourceLines = lines.slice(node.source.line - 1, node.source.lastLine);
const firstLine = sourceLines[0].slice(Math.max(0, node.source.column - 1));
const lastLine = sourceLines[sourceLines.length - 1].slice(0, node.source.lastColumn);
if (sourceLines.length > 2) {
return [firstLine, ...sourceLines.slice(1, sourceLines.length - 1), lastLine].join('\n');
}
else if (sourceLines.length == 2) {
return [firstLine, lastLine].join('\n');
}
else {
return firstLine;
}
}
getDepth(pathObject) {
let depth = 0;
let n = pathObject;
while (n) {
const keys = Object.keys(n);
if (keys.length > 0) {
depth++;
n = n[keys[0]];
}
else {
break;
}
}
return depth;
}
// When doing an exact match, need to check the path to the node
// and verify the hierarchy matches
matchesExact(pathObject, node, pathToNode) {
var _a;
const targetDepth = this.getDepth(pathObject);
const currentDepth = pathToNode.length;
if (currentDepth != targetDepth) {
return false;
}
let n = pathObject;
let m = pathToNode;
while (n && m) {
const key = Object.keys(n)[0];
if (key && key !== ((_a = m[0]) === null || _a === void 0 ? void 0 : _a.name)) {
return false;
}
n = n[key];
m = m.slice(1);
}
return true;
}
isTargetNode(node) {
return node.type === 'method' || node.type === 'variable';
}
async getJava() {
try {
if (process.env.JAVA_HOME) {
return (0, path_1.join)(process.env.JAVA_HOME, 'bin', 'java');
}
const v = await (0, subprocess_1.spawnCommand)('java', ['-version'], {
stdio: 'pipe',
combineStreams: true
});
if (!v) {
throw new Error('Unable to find java on PATH');
}
return 'java';
}
catch (e) {
}
return null;
}
getGradleParserPath() {
return (0, path_1.dirname)(require.resolve('@trapezedev/gradle-parse'));
}
async setApplicationId(applicationId) {
const source = await this.getGradleSource();
if (source) {
this.source = source.replace(/(applicationId\s+)["'][^"']+["']/, `$1"${applicationId}"`);
}
}
async getApplicationId() {
const source = await this.getGradleSource();
if (source) {
const applicationId = source.match(/applicationId\s+["']([^"']+)["']/);
if (!applicationId) {
return null;
}
return applicationId[1];
}
return null;
}
async setVersionCode(versionCode) {
const source = await this.getGradleSource();
if (source) {
logger_1.Logger.v('gradle', 'setVersionCode', `to ${versionCode} in ${this.filename}`);
return this.replaceProperties({
android: {
defaultConfig: {
versionCode: {}
}
}
}, {
versionCode
});
}
}
async getVersionCode() {
const source = await this.getGradleSource();
if (source) {
const versionCode = source.match(/versionCode\s+(\w+)/);
if (!versionCode) {
return null;
}
return parseInt(versionCode[1]);
}
return null;
}
async incrementVersionCode() {
const source = await this.getGradleSource();
if (source) {
const versionCode = source.match(/versionCode\s+(\w+)/);
if (!versionCode) {
return;
}
const num = parseInt(versionCode[1]);
if (!isNaN(num)) {
logger_1.Logger.v('gradle', 'incrementVersionCode', `to ${num} in ${this.filename}`);
return this.setVersionCode(num + 1);
}
}
}
async setVersionName(versionName) {
const source = await this.getGradleSource();
if (source) {
logger_1.Logger.v('gradle', 'setVersionName', `to ${versionName} in ${this.filename}`);
return this.replaceProperties({
android: {
defaultConfig: {
versionName: {}
}
}
}, {
versionName: `"${versionName}"`
});
}
}
async getVersionName() {
const source = await this.getGradleSource();
if (source) {
const versionName = source.match(/versionName\s+["']([^"']+)["']/) || null;
if (!versionName) {
return null;
}
return versionName[1];
}
return null;
}
async setVersionNameSuffix(versionNameSuffix) {
const source = await this.getGradleSource();
if (source) {
logger_1.Logger.v('gradle', 'setVersionNameSuffix', `to ${versionNameSuffix} in ${this.filename}`);
return this.replaceProperties({
android: {
defaultConfig: {
versionNameSuffix: {}
}
}
}, {
versionNameSuffix: `"${versionNameSuffix}"`
});
}
}
async getVersionNameSuffix() {
const source = await this.getGradleSource();
if (source) {
const versionName = source.match(/versionNameSuffix\s+["']([^"']+)["']/) || null;
if (!versionName) {
return null;
}
return versionName[1];
}
return null;
}
async getNamespace() {
const source = await this.getGradleSource();
if (source) {
const namespace = source.match(/namespace\s+["']([^"']+)["']/);
if (!namespace) {
return null;
}
return namespace[1];
}
return null;
}
async setNamespace(namespace) {
const source = await this.getGradleSource();
if (source) {
logger_1.Logger.v('gradle', 'setNamespace', `to ${namespace} in ${this.filename}`);
return this.replaceProperties({
android: {
namespace: {}
}
}, {
namespace: `"${namespace}"`
});
}
}
/*
Generate a fragment of Gradle/Groovy code given the inject object
A gradle edit will be of the form:
[
{
maven: [{
url: 'https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1',
name: 'Duo-SDK-Feed'
}]
}
]
*/
createGradleSource(injectObj, lines, indentation, depth = 0, targetNode, type) {
for (const entry of injectObj) {
const keys = Object.keys(entry);
for (const key of keys) {
const editEntry = entry[key];
if (Array.isArray(editEntry)) {
if (typeof editEntry[0] === 'object') {
lines.push(`${key} {`);
this.createGradleSource(editEntry, lines, indentation, depth + 1, targetNode, type);
lines.push('}');
}
else {
// Create a variable entry if the target node type is a variable or
// the provided type is a variable
if (targetNode.type === 'variable' || type === "variable" /* AndroidGradleInjectType.Variable */) {
lines.push(`${key} = ${JSON.stringify(editEntry)}`);
}
else {
lines.push(`${key} ${editEntry}`);
}
}
}
else if (typeof editEntry === 'string' ||
typeof editEntry === 'number' ||
typeof editEntry === 'boolean') {
if (targetNode.type === 'variable' || type === "variable" /* AndroidGradleInjectType.Variable */) {
lines.push((0, text_1.indent)(`${key} = ${editEntry}`, indentation, depth));
}
else {
lines.push((0, text_1.indent)(`${key} ${editEntry}`, indentation, depth));
}
}
else {
const fields = Object.keys(editEntry);
for (const fieldKey of fields) {
const fieldEntry = editEntry[fieldKey];
if (typeof fieldEntry === 'string') {
lines.push((0, text_1.indent)(`${fieldKey} ${fieldEntry}`, indentation, depth));
}
else if (Array.isArray(fieldEntry)) {
lines.push('{');
this.createGradleSource(fieldEntry, lines, indentation, depth + 1, targetNode, type);
lines.push('}');
}
}
}
}
}
}
async getGradleSource() {
var _a, _b;
const ref = this.vfs.get(this.filename);
if (ref) {
return (_b = (_a = ref.getData()) === null || _a === void 0 ? void 0 : _a.getDocument()) !== null && _b !== void 0 ? _b : '';
}
const contents = await (0, utils_fs_1.readFile)(this.filename, { encoding: 'utf-8' });
this.source = contents;
this.vfs.open(this.filename, this, this.gradleCommitFn, this.gradleDiffFn);
return contents;
}
gradleParseError() {
return `java not found on path and JAVA_HOME not set. Please set JAVA_HOME to the root of your Java installation.\n\nGradle parse functionality depends on a local Java install for accurate Gradle file modification.`;
}
}
exports.GradleFile = GradleFile;
//# sourceMappingURL=gradle-file.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,71 @@
/// <reference types="node" />
/// <reference types="node" />
import { MobileProject } from '../project';
import { AndroidResDir } from '../definitions';
import { GradleFile } from './gradle-file';
import { XmlFile } from '../xml';
import { PropertiesFile } from '../properties';
import { PlatformProject } from '../platform-project';
export declare class AndroidProject extends PlatformProject {
private manifest;
private buildGradle;
private appBuildGradle;
constructor(project: MobileProject);
load(): Promise<void>;
getBuildGradle(): GradleFile | null;
getAppBuildGradle(): GradleFile | null;
getAndroidManifest(): XmlFile;
/**
* Get a project file container for the given path in the project root.
* This will return an existing file container or create a new one.
*/
getProjectFile<T>(path: string, create: (filename: string) => T): T | null;
getResourceXmlFile(resourcePath: string): XmlFile | null;
getXmlFile(path: string): XmlFile | null;
getPropertiesFile(path: string): PropertiesFile | null;
getGradleFile(path: string): Promise<GradleFile | null>;
setAppName(appName: string): Promise<void>;
/**
* Update the Android package name. This renames the package in `AndroidManifest.xml`,
* the `applicationId` in `app/build.gradle`, and renames the java
* package for the `MainActivity.java` file.
*
* This action will mutate the project on disk!
*/
setPackageName(packageName: string): Promise<void>;
getMainActivityFilename(): string;
getMainActivityPath(): Promise<string>;
getGradlePluginVersion(): Promise<string | null>;
getPackageName(): Promise<string | null | undefined>;
setVersionCode(versionCode: number): Promise<void> | undefined;
getVersionCode(): Promise<number | null>;
incrementVersionCode(): Promise<void>;
setVersionName(versionName: string): Promise<void> | undefined;
getVersionName(): Promise<string | null>;
setVersionNameSuffix(versionNameSuffix: string): Promise<void> | undefined;
getVersionNameSuffix(): Promise<string | null>;
/**
* Add a new file to the given resources directory with the given contents and
* given file name
**/
getResource(resDir: AndroidResDir, file: string, options?: {
encoding: 'utf-8' | string;
} | null): Promise<string> | Promise<Buffer> | undefined;
/**
* Add a new file to the given resources directory with the given contents and
* given file name
**/
addResource(resDir: AndroidResDir, file: string, contents: string): Promise<void>;
copyFile(src: string, dest: string): Promise<void>;
/**
* Copy the given source into the given resources directory with the
* given file name
**/
copyToResources(resDir: AndroidResDir, file: string, source: string): Promise<void>;
private getAndroidManifestPath;
getResourcesPath(): string;
getResourcesRoot(): string | null;
private getAppRoot;
private loadGradle;
}
//# sourceMappingURL=project.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/android/project.ts"],"names":[],"mappings":";;AAaA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAKtD,qBAAa,cAAe,SAAQ,eAAe;IACjD,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,cAAc,CAA2B;gBAErC,OAAO,EAAE,aAAa;IAO5B,IAAI;IAUV,cAAc;IAId,iBAAiB;IAIjB,kBAAkB;IAIlB;;;OAGG;IACH,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI;IAkB1E,kBAAkB,CAAC,YAAY,EAAE,MAAM;IAIvC,UAAU,CAAC,IAAI,EAAE,MAAM;IAOvB,iBAAiB,CAAC,IAAI,EAAE,MAAM;IAOxB,aAAa,CAAC,IAAI,EAAE,MAAM;IAU1B,UAAU,CAAC,OAAO,EAAE,MAAM;IAkChC;;;;;;OAMG;IACG,cAAc,CAAC,WAAW,EAAE,MAAM;IA6FxC,uBAAuB;IAejB,mBAAmB;IAQnB,sBAAsB;IAkBtB,cAAc;IAUpB,cAAc,CAAC,WAAW,EAAE,MAAM;IAQ5B,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI9C,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrC,cAAc,CAAC,WAAW,EAAE,MAAM;IAIlC,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIxC,oBAAoB,CAAC,iBAAiB,EAAE,MAAM;IAI9C,oBAAoB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI9C;;;QAGI;IACJ,WAAW,CACT,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;QAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAA;KAAE,GAAG,IAA4B;IAexE;;;QAGI;IACE,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAiBjE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBxD;;;QAGI;IACE,eAAe,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAkBzE,OAAO,CAAC,sBAAsB;IAa9B,gBAAgB,IAAI,MAAM;IAI1B,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAQjC,OAAO,CAAC,UAAU;YASJ,UAAU;CAQzB"}

View file

@ -0,0 +1,348 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AndroidProject = void 0;
const path_1 = require("path");
const utils_fs_1 = require("@ionic/utils-fs");
const gradle_file_1 = require("./gradle-file");
const xml_1 = require("../xml");
const properties_1 = require("../properties");
const platform_project_1 = require("../platform-project");
const read_src_1 = require("../read-src");
const logger_1 = require("../logger");
class AndroidProject extends platform_project_1.PlatformProject {
constructor(project) {
super(project);
this.buildGradle = null;
this.appBuildGradle = null;
const manifestPath = this.getAndroidManifestPath();
this.manifest = new xml_1.XmlFile(manifestPath, project.vfs);
}
async load() {
try {
await this.manifest.load();
this.buildGradle = await this.loadGradle('build.gradle');
this.appBuildGradle = await this.loadGradle('app/build.gradle');
}
catch (e) {
this.setError(e);
}
}
getBuildGradle() {
return this.buildGradle;
}
getAppBuildGradle() {
return this.appBuildGradle;
}
getAndroidManifest() {
return this.manifest;
}
/**
* Get a project file container for the given path in the project root.
* This will return an existing file container or create a new one.
*/
getProjectFile(path, create) {
var _a;
const root = (_a = this.project.config.android) === null || _a === void 0 ? void 0 : _a.path;
if (!root) {
return null;
}
const filename = (0, path_1.join)(root, path);
const existing = this.project.vfs.get(filename);
if (existing) {
return existing.getData();
}
return create(filename);
}
getResourceXmlFile(resourcePath) {
return this.getXmlFile((0, path_1.join)(this.getResourcesPath(), resourcePath));
}
getXmlFile(path) {
return this.getProjectFile(path, (filename) => new xml_1.XmlFile(filename, this.project.vfs));
}
getPropertiesFile(path) {
return this.getProjectFile(path, (filename) => new properties_1.PropertiesFile(filename, this.project.vfs));
}
async getGradleFile(path) {
if (path === 'build.gradle') {
return this.buildGradle;
}
else if (path === 'app/build.gradle') {
return this.appBuildGradle;
}
return this.loadGradle(path);
}
async setAppName(appName) {
const application = this.manifest.find('manifest/application');
if (!application) {
logger_1.Logger.v('android', 'setAppName', `No <application> node found in <manifest>`);
return;
}
const label = application[0].getAttribute('android:label');
logger_1.Logger.v('android', 'setAppName', `current app label is ${label}`);
if (label) {
if (label.indexOf('@string') === 0) {
logger_1.Logger.v('android', 'setAppName', 'android:label pointing to strings.xml resource file. Reading values/strings.xml');
const stringsFile = await this.getResourceXmlFile('values/strings.xml');
if (!stringsFile) {
logger_1.Logger.v('android', 'setAppName', 'Unable to load values/strings.xml resource file');
return;
}
await stringsFile.load();
const attr = label.replace('@string/', '');
// TODO: use the value specified in the @strings attribute
logger_1.Logger.v('android', 'setAppName', `Updated values/strings.xml <string name="${attr}"> to <string name="${attr}">${appName}</string>`);
stringsFile.replaceFragment(`resources/string[@name="${attr}"]`, `<string name="${attr}">${appName}</string>`);
}
}
else {
logger_1.Logger.v('android', 'setAppName', `No android:label on <application> node, setting value directly`);
application[0].setAttribute('android:label', appName);
}
}
/**
* Update the Android package name. This renames the package in `AndroidManifest.xml`,
* the `applicationId` in `app/build.gradle`, and renames the java
* package for the `MainActivity.java` file.
*
* This action will mutate the project on disk!
*/
async setPackageName(packageName) {
var _a, _b, _c, _d, _e, _f;
const sourceDir = (0, path_1.join)(this.getAppRoot(), 'src', 'main', 'java');
let hadPackageAttr = false;
let oldPackageName = await ((_a = this.manifest
.getDocumentElement()) === null || _a === void 0 ? void 0 : _a.getAttribute('package'));
if (!oldPackageName) {
oldPackageName = await ((_b = this.appBuildGradle) === null || _b === void 0 ? void 0 : _b.getApplicationId());
}
else {
hadPackageAttr = true;
}
const oldPackageParts = (_c = oldPackageName === null || oldPackageName === void 0 ? void 0 : oldPackageName.split('.')) !== null && _c !== void 0 ? _c : [];
logger_1.Logger.v('android', 'setPackageName', 'setting Android package name to', packageName, 'from', oldPackageName);
if (packageName === oldPackageName) {
return;
}
const existingPackage = (0, path_1.join)(sourceDir, ...oldPackageParts);
if (!(await (0, utils_fs_1.pathExists)(existingPackage))) {
throw new Error('Current Java package name and directory structure do not match the <manifest> package attribute. Ensure these match before modifying the project package name');
}
let activityName = '.MainActivity';
if (hadPackageAttr) {
(_d = this.manifest.getDocumentElement()) === null || _d === void 0 ? void 0 : _d.setAttribute('package', packageName);
activityName = `${packageName}.MainActivity`;
}
await ((_e = this.appBuildGradle) === null || _e === void 0 ? void 0 : _e.setApplicationId(packageName));
await ((_f = this.appBuildGradle) === null || _f === void 0 ? void 0 : _f.setNamespace(packageName));
logger_1.Logger.v('android', 'setPackageName', `set manifest package attribute and applicationId to ${packageName}`);
this.manifest.setAttrs('manifest/application/activity', {
'android:name': activityName
});
logger_1.Logger.v('android', 'setPackageName', `set <activity android:name="${packageName}.MainActivity"`);
if (!this.getAppRoot()) {
return;
}
const newPackageParts = packageName.split('.');
const destDir = (0, path_1.join)(sourceDir, ...newPackageParts);
const mainActivityName = this.getMainActivityFilename();
logger_1.Logger.v('android', 'setPackageName', `Got main activity name ${mainActivityName}`);
let activityFile = (0, path_1.join)(sourceDir, ...oldPackageParts, mainActivityName);
logger_1.Logger.v('android', 'setPackageName', `Looking for old activity file at ${activityFile}`);
// Make the new directory tree and any missing parents
await (0, utils_fs_1.mkdirp)(destDir);
// Move the old activity file over
await (0, utils_fs_1.move)(activityFile, (0, path_1.join)(destDir, 'MainActivity.java'));
// Try to delete the empty directories we left behind, starting
// from the deepest
let sourceDirLeaf = (0, path_1.join)(sourceDir, ...oldPackageParts);
logger_1.Logger.v('android', 'setPackageName', `removing old source dirs for old package (${sourceDirLeaf})`);
for (const _ of oldPackageParts) {
try {
await (0, utils_fs_1.rmdir)(sourceDirLeaf);
}
catch (ex) {
// This will fail if directory is not empty, that's fine, we won't delete those
}
sourceDirLeaf = (0, path_1.join)(sourceDirLeaf, '..');
}
// Rename the package in the main source file
activityFile = (0, path_1.join)(sourceDir, ...newPackageParts, this.getMainActivityFilename());
if (await (0, utils_fs_1.pathExists)(activityFile)) {
logger_1.Logger.v('android', 'setPackageName', `renaming package in source for activity file ${activityFile}`);
const activitySource = await (0, utils_fs_1.readFile)(activityFile, {
encoding: 'utf-8',
});
const newActivitySource = activitySource === null || activitySource === void 0 ? void 0 : activitySource.replace(/(package\s+)[^;]+/, `$1${packageName}`);
await (0, utils_fs_1.writeFile)(activityFile, newActivitySource);
}
}
getMainActivityFilename() {
const activity = this.manifest.find('manifest/application/activity');
if (!activity) {
return 'MainActivity.java';
}
const activityName = activity[0].getAttribute('android:name');
const parts = activityName === null || activityName === void 0 ? void 0 : activityName.split('.');
if (!parts) {
return '';
}
return `${parts[parts.length - 1]}.java`;
}
async getMainActivityPath() {
var _a, _b;
const packageName = await ((_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.getApplicationId());
const activityName = this.getMainActivityFilename();
const packageParts = (_b = packageName === null || packageName === void 0 ? void 0 : packageName.split('.')) !== null && _b !== void 0 ? _b : [];
return (0, path_1.join)('app', 'src', 'main', 'java', ...packageParts, activityName);
}
async getGradlePluginVersion() {
var _a, _b, _c, _d;
await ((_a = this.buildGradle) === null || _a === void 0 ? void 0 : _a.parse());
const found = (_b = this.buildGradle) === null || _b === void 0 ? void 0 : _b.find({
buildscript: {
dependencies: {
classpath: {}
}
}
});
const sources = (found !== null && found !== void 0 ? found : []).map(f => { var _a, _b; return (_b = (_a = this.buildGradle) === null || _a === void 0 ? void 0 : _a.getSource(f.node)) !== null && _b !== void 0 ? _b : ''; });
const gradleLine = sources.find(s => s.indexOf('com.android.tools.build:gradle:'));
return (_d = (_c = gradleLine === null || gradleLine === void 0 ? void 0 : gradleLine.match(/:([\d.]+)/)) === null || _c === void 0 ? void 0 : _c[1]) !== null && _d !== void 0 ? _d : null;
}
async getPackageName() {
var _a, _b;
const namespace = await ((_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.getNamespace());
if (namespace) {
return namespace;
}
return (_b = this.manifest.getDocumentElement()) === null || _b === void 0 ? void 0 : _b.getAttribute('package');
}
setVersionCode(versionCode) {
var _a;
if (versionCode === '') {
versionCode = 1;
}
return (_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.setVersionCode(typeof versionCode === 'number' ? versionCode : parseInt(versionCode, 10));
}
async getVersionCode() {
var _a, _b;
return (_b = (await ((_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.getVersionCode()))) !== null && _b !== void 0 ? _b : null;
}
incrementVersionCode() {
var _a, _b;
return (_b = (_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.incrementVersionCode()) !== null && _b !== void 0 ? _b : Promise.resolve();
}
setVersionName(versionName) {
var _a;
return (_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.setVersionName(versionName);
}
getVersionName() {
var _a, _b;
return (_b = (_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.getVersionName()) !== null && _b !== void 0 ? _b : Promise.resolve(null);
}
setVersionNameSuffix(versionNameSuffix) {
var _a;
return (_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.setVersionNameSuffix(versionNameSuffix);
}
getVersionNameSuffix() {
var _a, _b;
return (_b = (_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.getVersionNameSuffix()) !== null && _b !== void 0 ? _b : Promise.resolve(null);
}
/**
* Add a new file to the given resources directory with the given contents and
* given file name
**/
getResource(resDir, file, options = { encoding: 'utf-8' }) {
const root = this.getResourcesRoot();
if (!root) {
return;
}
const dir = (0, path_1.join)(root, resDir);
if (!options) {
return (0, utils_fs_1.readFile)((0, path_1.join)(dir, file));
}
return (0, utils_fs_1.readFile)((0, path_1.join)(dir, file), options);
}
/**
* Add a new file to the given resources directory with the given contents and
* given file name
**/
async addResource(resDir, file, contents) {
const root = this.getResourcesRoot();
if (!root) {
return;
}
const dir = (0, path_1.join)(root, resDir);
logger_1.Logger.v(`android`, 'addResource', `add res file ${file} to ${resDir}`);
if (!(await (0, utils_fs_1.pathExists)(dir))) {
await (0, utils_fs_1.mkdir)(dir);
}
return (0, utils_fs_1.writeFile)((0, path_1.join)(dir, file), contents);
}
async copyFile(src, dest) {
var _a, _b, _c;
if (!((_c = (_b = (_a = this.project) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.android) === null || _c === void 0 ? void 0 : _c.path)) {
return Promise.reject();
}
const destPath = (0, path_1.join)(this.project.config.android.path, dest);
logger_1.Logger.v(`android`, `copyFile`, `copying ${src} to ${destPath}`);
if (/^(https?:\/\/)/.test(src)) {
const res = await fetch(src);
return (0, utils_fs_1.writeFile)(destPath, Buffer.from(await res.arrayBuffer()));
}
const srcPath = (0, path_1.join)(this.project.config.android.path, src);
return (0, utils_fs_1.copy)(srcPath, destPath);
}
/**
* Copy the given source into the given resources directory with the
* given file name
**/
async copyToResources(resDir, file, source) {
const root = this.getResourcesRoot();
if (!root) {
return;
}
const dir = (0, path_1.join)(root, resDir);
if (!(await (0, utils_fs_1.pathExists)(dir))) {
await (0, utils_fs_1.mkdir)(dir);
}
logger_1.Logger.v(`android`, `copyToResources`, `copying ${file} to Android resources at ${(0, path_1.join)(dir, file)}`);
const sourceData = await (0, read_src_1.readSource)(source);
return (0, utils_fs_1.writeFile)((0, path_1.join)(dir, file), sourceData);
}
getAndroidManifestPath() {
var _a, _b;
if (!((_a = this.project.config.android) === null || _a === void 0 ? void 0 : _a.path)) {
return null;
}
return (0, path_1.join)((_b = this.project.config.android) === null || _b === void 0 ? void 0 : _b.path, 'app', 'src', 'main', 'AndroidManifest.xml');
}
getResourcesPath() {
return (0, path_1.join)('app', 'src', 'main', 'res');
}
getResourcesRoot() {
var _a, _b;
if (!((_a = this.project.config.android) === null || _a === void 0 ? void 0 : _a.path)) {
return null;
}
return (0, path_1.join)((_b = this.project.config.android) === null || _b === void 0 ? void 0 : _b.path, this.getResourcesPath());
}
getAppRoot() {
var _a, _b;
if (!((_a = this.project.config.android) === null || _a === void 0 ? void 0 : _a.path)) {
return null;
}
// TODO: Don't hard-code app
return (0, path_1.join)((_b = this.project.config.android) === null || _b === void 0 ? void 0 : _b.path, 'app');
}
async loadGradle(path) {
var _a, _b;
if (!((_a = this.project.config.android) === null || _a === void 0 ? void 0 : _a.path)) {
return null;
}
const filename = (0, path_1.join)((_b = this.project.config.android) === null || _b === void 0 ? void 0 : _b.path, path);
return new gradle_file_1.GradleFile(filename, this.project.vfs);
}
}
exports.AndroidProject = AndroidProject;
//# sourceMappingURL=project.js.map

File diff suppressed because one or more lines are too long