Skip to content

feat: config option #5554

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/man_pages/start.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,6 @@ Option | Description
-------|---------
--help, -h, /? | Prints help about the selected command in the console.
--path `<Directory>` | Specifies the directory that contains the project. If not set, the project is searched for in the current directory and all directories above it.
--config | Specifies the name of the Nativescript configuration file to load (relative to the project directory). The default is `nativescript.config.ts` or `nativescript.config.js` (as a fallback).
--version | Prints the client version.
--log trace | Prints a detailed diagnostic log for the execution of the current command.
2 changes: 1 addition & 1 deletion lib/declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,10 +616,10 @@ interface IOptions
/**
* Project Configuration
*/
config: string[];
log: string;
verbose: boolean;
path: string;
config: string;
version: boolean;
help: boolean;
json: boolean;
Expand Down
1 change: 1 addition & 0 deletions lib/definitions/project.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ interface INsConfig {
webpackConfigPath?: string;
ios?: INsConfigIOS;
android?: INsConfigAndroid;
ignoredNativeDependencies?: string[];
}

interface IProjectData extends ICreateProjectData {
Expand Down
1 change: 1 addition & 0 deletions lib/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export class Options {
profileDir: { type: OptionType.String, hasSensitiveValue: true },
analyticsClient: { type: OptionType.String, hasSensitiveValue: false },
path: { type: OptionType.String, alias: "p", hasSensitiveValue: true },
config: { type: OptionType.String, alias: "c", hasSensitiveValue: true },
// This will parse all non-hyphenated values as strings.
_: { type: OptionType.String, hasSensitiveValue: false },
};
Expand Down
2 changes: 2 additions & 0 deletions lib/project-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export class ProjectData implements IProjectData {
public appResourcesDirectoryPath: string;
public dependencies: any;
public devDependencies: IStringDictionary;
public ignoredDependencies: string[];
public projectType: string;
public androidManifestPath: string;
public infoPlistPath: string;
Expand Down Expand Up @@ -172,6 +173,7 @@ export class ProjectData implements IProjectData {
this.devDependencies = packageJsonData.devDependencies;
this.projectType = this.getProjectType();
this.nsConfig = nsConfig;
this.ignoredDependencies = nsConfig?.ignoredNativeDependencies;
this.appDirectoryPath = this.getAppDirectoryPath();
this.appResourcesDirectoryPath = this.getAppResourcesDirectoryPath();
this.androidManifestPath = this.getPathToAndroidManifest(
Expand Down
121 changes: 103 additions & 18 deletions lib/services/project-config-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,23 +93,108 @@ export default {
);
}

private getConfigPathsFromPossiblePaths(paths: {
[key: string]: string[];
}): any {
const {
possibleTSConfigPaths,
possibleJSConfigPaths,
possibleNSConfigPaths,
} = paths;

let TSConfigPath;
let JSConfigPath;
let NSConfigPath;

// look up a ts config first
TSConfigPath = possibleTSConfigPaths
.filter(Boolean)
.find((path) => this.$fs.exists(path));

// if not found, look up a JS config
if (!TSConfigPath) {
JSConfigPath = possibleJSConfigPaths
.filter(Boolean)
.find((path) => this.$fs.exists(path));
}

// lastly look for nsconfig/json config
if (!TSConfigPath && !JSConfigPath) {
NSConfigPath = possibleNSConfigPaths
.filter(Boolean)
.find((path) => this.$fs.exists(path));
}

return {
TSConfigPath,
JSConfigPath,
NSConfigPath,
found: TSConfigPath || JSConfigPath || NSConfigPath,
};
}

public detectProjectConfigs(projectDir?: string): IProjectConfigInformation {
const JSConfigPath = path.join(
projectDir || this.projectHelper.projectDir,
CONFIG_FILE_NAME_JS
);
const TSConfigPath = path.join(
projectDir || this.projectHelper.projectDir,
CONFIG_FILE_NAME_TS
);
const NSConfigPath = path.join(
projectDir || this.projectHelper.projectDir,
CONFIG_NS_FILE_NAME
);
const possibleTSConfigPaths = [];
const possibleJSConfigPaths = [];
const possibleNSConfigPaths = [];
let paths;

// allow overriding config name with env variable or --config (or -c)
const configFilename =
process.env.NATIVESCRIPT_CONFIG_NAME ?? this.$options.config;
if (configFilename) {
const fullPath = this.$fs.isRelativePath(configFilename)
? path.join(projectDir || this.projectHelper.projectDir, configFilename)
: configFilename;

possibleTSConfigPaths.unshift(
fullPath.endsWith(".ts") ? fullPath : `${fullPath}.ts`
);
possibleJSConfigPaths.unshift(
fullPath.endsWith(".js") ? fullPath : `${fullPath}.js`
);
possibleNSConfigPaths.unshift(
fullPath.endsWith(".json") ? fullPath : `${fullPath}.json`
);

const hasTSConfig = this.$fs.exists(TSConfigPath);
const hasJSConfig = this.$fs.exists(JSConfigPath);
const hasNSConfig = this.$fs.exists(NSConfigPath);
paths = this.getConfigPathsFromPossiblePaths({
possibleTSConfigPaths,
possibleJSConfigPaths,
possibleNSConfigPaths,
});
}

// look up default paths if no path found yet
if (!paths?.found) {
possibleTSConfigPaths.push(
path.join(
projectDir || this.projectHelper.projectDir,
CONFIG_FILE_NAME_TS
)
);
possibleJSConfigPaths.push(
path.join(
projectDir || this.projectHelper.projectDir,
CONFIG_FILE_NAME_JS
)
);
possibleNSConfigPaths.push(
path.join(
projectDir || this.projectHelper.projectDir,
CONFIG_NS_FILE_NAME
)
);

paths = this.getConfigPathsFromPossiblePaths({
possibleTSConfigPaths,
possibleJSConfigPaths,
possibleNSConfigPaths,
});
}

const hasTSConfig = !!paths.TSConfigPath;
const hasJSConfig = !!paths.JSConfigPath;
const hasNSConfig = !!paths.NSConfigPath;
const usingNSConfig = !(hasTSConfig || hasJSConfig);

if (hasTSConfig && hasJSConfig) {
Expand All @@ -123,9 +208,9 @@ export default {
hasJSConfig,
hasNSConfig,
usingNSConfig,
TSConfigPath,
JSConfigPath,
NSConfigPath,
TSConfigPath: paths.TSConfigPath,
JSConfigPath: paths.JSConfigPath,
NSConfigPath: paths.NSConfigPath,
};
}

Expand Down
9 changes: 9 additions & 0 deletions lib/services/webpack/webpack-compiler-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import {
IPackageManager,
IPackageInstallationManager,
IOptions,
} from "../../declarations";
import { IPlatformData } from "../../definitions/platform";
import { IProjectData } from "../../definitions/project";
Expand Down Expand Up @@ -48,6 +49,7 @@ export class WebpackCompilerService
private expectedHashes: IStringDictionary = {};

constructor(
private $options: IOptions,
private $errors: IErrors,
private $childProcess: IChildProcess,
public $fs: IFileSystem,
Expand Down Expand Up @@ -368,6 +370,13 @@ export class WebpackCompilerService

envData.verbose = envData.verbose || this.$logger.isVerbose();
envData.production = envData.production || prepareData.release;

// add the config file name to the env data so the webpack process can read the
// correct config file when resolving the CLI lib and the config service
// we are explicityly setting it to false to force using the defaults
envData.config =
process.env.NATIVESCRIPT_CONFIG_NAME ?? this.$options.config ?? "false";

// The snapshot generation is wrongly located in the Webpack plugin.
// It should be moved in the Native Prepare of the CLI or a Gradle task in the Runtime.
// As a workaround, we skip the mksnapshot, xxd and android-ndk calls based on skipNativePrepare.
Expand Down
18 changes: 18 additions & 0 deletions test/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,24 @@ describe("options", () => {
assert.isTrue(isExecutionStopped);
});

it("does not break execution when valid option has correct value", () => {
process.argv.push("--config");
process.argv.push("SomeFilename");
const options = createOptions(testInjector);
options.validateOptions();
process.argv.pop();
process.argv.pop();
assert.isFalse(isExecutionStopped);
});

it("breaks execution when valid option has empty string value", () => {
process.argv.push("--config");
const options = createOptions(testInjector);
options.validateOptions();
process.argv.pop();
assert.isTrue(isExecutionStopped);
});

it("breaks execution when valid option has value with spaces only", () => {
process.argv.push("--path");
process.argv.push(" ");
Expand Down
78 changes: 78 additions & 0 deletions test/services/project-config-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const createTestInjector = (

readJson: (filePath: string): any => null,

isRelativePath: (filePath: string): any => true,

enumerateFilesInDirectorySync: (
directoryPath: string,
filterCallback?: (_file: string, _stat: IFsStats) => boolean,
Expand Down Expand Up @@ -141,6 +143,82 @@ describe("projectConfigService", () => {
assert.deepStrictEqual(actualValue, "--expose-gc");
});

it("can read a named JS config file when passing --config", async () => {
const testInjector = createTestInjector(
(filename) => sampleJSConfig,
(filePath) => basename(filePath) === "custom.config.js"
);

// mock "--config custom.config.js"
const options: Options = testInjector.resolve("options") as Options;
// @ts-ignore
options.config = "custom.config.js";

const projectConfigService: IProjectConfigService = testInjector.resolve(
"projectConfigService"
);

const actualValue = projectConfigService.getValue("id");
assert.deepStrictEqual(actualValue, "io.test.app");
});

it("can read a named TS config file when passing --config", async () => {
const testInjector = createTestInjector(
(filename) => sampleTSConfig,
(filePath) => basename(filePath) === "custom.config.ts"
);

// mock "--config custom.config.ts"
const options: Options = testInjector.resolve("options") as Options;
// @ts-ignore
options.config = "custom.config.ts";

const projectConfigService: IProjectConfigService = testInjector.resolve(
"projectConfigService"
);

const actualValue = projectConfigService.getValue("id");
assert.deepStrictEqual(actualValue, "io.test.app");
});

it("can read a named JS config file when passing --config without extension", async () => {
const testInjector = createTestInjector(
(filename) => sampleJSConfig,
(filePath) => basename(filePath) === "custom.config.js"
);

// mock "--config custom.config"
const options: Options = testInjector.resolve("options") as Options;
// @ts-ignore
options.config = "custom.config";

const projectConfigService: IProjectConfigService = testInjector.resolve(
"projectConfigService"
);

const actualValue = projectConfigService.getValue("id");
assert.deepStrictEqual(actualValue, "io.test.app");
});

it("can read a named TS config file when passing --config without extension", async () => {
const testInjector = createTestInjector(
(filename) => sampleTSConfig,
(filePath) => basename(filePath) === "custom.config.ts"
);

// mock "--config custom.config"
const options: Options = testInjector.resolve("options") as Options;
// @ts-ignore
options.config = "custom.config";

const projectConfigService: IProjectConfigService = testInjector.resolve(
"projectConfigService"
);

const actualValue = projectConfigService.getValue("id");
assert.deepStrictEqual(actualValue, "io.test.app");
});

// it("Throws error if no config file found", () => {
// const testInjector = createTestInjector(
// (filename) => null,
Expand Down
1 change: 1 addition & 0 deletions test/services/webpack/webpack-compiler-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function createTestInjector(): IInjector {
testInjector.register("childProcess", {});
testInjector.register("hooksService", {});
testInjector.register("hostInfo", {});
testInjector.register("options", {});
testInjector.register("logger", {});
testInjector.register("errors", ErrorsStub);
testInjector.register("packageInstallationManager", {});
Expand Down