Skip to content
This repository was archived by the owner on Apr 13, 2025. It is now read-only.

Commit cb3d43c

Browse files
authored
Merge pull request #362 from codeoverflow-org/feat/nodecg-v2-support
Support for NodeCG v2
2 parents fb00982 + ed10299 commit cb3d43c

File tree

11 files changed

+227
-50
lines changed

11 files changed

+227
-50
lines changed

.devcontainer/devcontainer.json

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,24 @@
66
"dockerfile": "Dockerfile"
77
},
88

9-
// Set *default* container specific settings.json values on container create.
10-
"settings": {
11-
"terminal.integrated.shell.linux": "/bin/zsh"
9+
"customizations": {
10+
"vscode": {
11+
"extensions": ["dbaeumer.vscode-eslint", "Orta.vscode-jest"],
12+
"settings": {
13+
"terminal.integrated.profiles.linux": {
14+
"bash": {
15+
"path": "bash",
16+
"icon": "terminal-bash"
17+
},
18+
"zsh": {
19+
"path": "zsh"
20+
}
21+
},
22+
"terminal.integrated.defaultProfile.linux": "zsh"
23+
}
24+
}
1225
},
1326

14-
// Add the IDs of extensions you want installed when the container is created.
15-
"extensions": ["dbaeumer.vscode-eslint"],
16-
1727
// Use 'forwardPorts' to make a list of ports inside the container available locally.
1828
// 9090 is the default nodecg port if you have setup a nodecg install using the install command and want to test it
1929
"forwardPorts": [9090],

.devcontainer/postCreate.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ npm link
88
# Get nodecg so you can test the cli using this installation
99
[ ! -d "nodecg" ] && git clone https://github.com/nodecg/nodecg.git
1010

11-
cd nodecg && npm i --omit=dev
11+
cd nodecg && npm i && npm run build

integration_test.sh

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,14 @@ function clean_test_directory {
2222
trap clean_test_directory EXIT
2323

2424
cd $dir
25-
git clone https://github.com/nodecg/nodecg.git --depth 1 .
25+
26+
# Download NodeCG
27+
if [ "$version" == "0.1" ] || [ "$version" == "0.2" ]; then
28+
# if version is 0.1 or 0.2 then switch back to NodeCG v1
29+
git clone https://github.com/nodecg/nodecg.git --depth 1 --branch legacy-1.x .
30+
else
31+
git clone https://github.com/nodecg/nodecg.git --depth 1 .
32+
fi
2633

2734
if [ "$version" == "development" ]; then
2835
nodecg-io install --nodecg-io-version $version --docs

src/generate/extension.ts

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,20 @@ export async function genExtension(opts: GenerationOptions, install: Installatio
4242
const writer = new CodeBlockWriter();
4343

4444
// imports
45-
genImport(writer, "requireService", opts.corePackage.name, opts.language);
45+
genImport(writer, "requireService", opts.corePackage.name, opts.language, false);
4646

4747
if (opts.language === "typescript") {
48-
genImport(writer, "NodeCG", `${opts.nodeCGTypingsPackage}/types/server`, opts.language);
48+
generateNodeCGImport(writer, opts, install);
49+
4950
// Service import statements
5051
services.forEach((svc) => {
51-
genImport(writer, svc.clientName, svc.packageName, opts.language);
52+
genImport(writer, svc.clientName, svc.packageName, opts.language, false);
5253
});
5354
}
5455

5556
// global nodecg function
5657
writer.blankLine();
57-
const nodecgVariableType = opts.language === "typescript" ? ": NodeCG" : "";
58-
writer.write(`module.exports = function (nodecg${nodecgVariableType}) `).block(() => {
58+
writer.write(`module.exports = function (nodecg${getNodeCGType(opts, install)}) `).block(() => {
5959
genLog(writer, `${opts.bundleName} bundle started.`);
6060
writer.blankLine();
6161

@@ -76,18 +76,68 @@ export async function genExtension(opts: GenerationOptions, install: Installatio
7676
await writeBundleFile(writer.toString(), opts.bundlePath, "extension", `index.${fileExtension}`);
7777
}
7878

79-
function genImport(writer: CodeBlockWriter, symbolToImport: string, packageName: string, lang: CodeLanguage) {
79+
function genImport(
80+
writer: CodeBlockWriter,
81+
symbolToImport: string,
82+
packageName: string,
83+
lang: CodeLanguage,
84+
isDefaultImport: boolean,
85+
) {
8086
if (lang === "typescript") {
81-
writer.write(`import { ${symbolToImport} } from `).quote(packageName).write(";");
87+
writer.write("import ");
88+
89+
if (!isDefaultImport) {
90+
writer.write("{ ");
91+
}
92+
writer.write(symbolToImport);
93+
if (!isDefaultImport) {
94+
writer.write(" }");
95+
}
96+
97+
writer.write(` from `).quote(packageName).write(";");
8298
} else if (lang === "javascript") {
83-
writer.write(`const ${symbolToImport} = require(`).quote(packageName).write(`).${symbolToImport};`);
99+
writer.write(`const ${symbolToImport} = require(`).quote(packageName).write(")");
100+
101+
if (!isDefaultImport) {
102+
writer.write(`.${symbolToImport}`);
103+
}
104+
105+
writer.write(";");
84106
} else {
85107
throw new Error("unsupported language: " + lang);
86108
}
87109

88110
writer.write("\n");
89111
}
90112

113+
export function determineNodeCGImportPath(opts: GenerationOptions, install: Installation): string {
114+
if (install.version === "0.1") {
115+
// nodecg-io 0.1 is only compatible with the NodeCG typings bundled inside the full nodecg package
116+
return "nodecg/types/server";
117+
} else if (install.version === "0.2" || opts.nodeCGVersion.major === 1) {
118+
// nodecg-io 0.2 is only compatible with nodecg-types.
119+
// Newer versions are compatible with both: nodecg-types (NodeCG v1) and @nodecg/types (NodeCG v2)
120+
// There we check the current nodecg version to determine which import to use.
121+
return "nodecg-types/types/server";
122+
} else if (opts.nodeCGVersion.major === 2) {
123+
// All versions from 0.3 and upwards support the official @nodecg/types package for NodeCG v2
124+
return "@nodecg/types";
125+
} else {
126+
throw new Error(
127+
"unable to determine nodecg typings import for nodecg " +
128+
opts.nodeCGVersion +
129+
" and nodecg-io " +
130+
install.version,
131+
);
132+
}
133+
}
134+
135+
function generateNodeCGImport(writer: CodeBlockWriter, opts: GenerationOptions, install: Installation) {
136+
const importPath = determineNodeCGImportPath(opts, install);
137+
const isDefaultImport = opts.nodeCGVersion.major === 2 && install.version !== "0.1" && install.version !== "0.2";
138+
genImport(writer, "NodeCG", importPath, opts.language, isDefaultImport);
139+
}
140+
91141
function genLog(writer: CodeBlockWriter, logMessage: string) {
92142
writer.write("nodecg.log.info(").quote(logMessage).write(");");
93143
}
@@ -121,3 +171,15 @@ function genOnUnavailableCall(writer: CodeBlockWriter, svc: ServiceNames) {
121171
})
122172
.write(");");
123173
}
174+
175+
function getNodeCGType(opts: GenerationOptions, install: Installation): string {
176+
if (opts.language !== "typescript") {
177+
return "";
178+
}
179+
180+
if (install.version === "0.1" || install.version === "0.2" || opts.nodeCGVersion.major === 1) {
181+
return ": NodeCG";
182+
} else {
183+
return ": NodeCG.ServerAPI";
184+
}
185+
}

src/generate/packageJson.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { genNodeCGDashboardConfig, genNodeCGGraphicConfig } from "./panel";
55
import { SemVer } from "semver";
66
import { writeBundleFile } from "./utils";
77
import { Installation } from "../utils/installation";
8+
import { determineNodeCGImportPath } from "./extension";
89

910
// Loaction where the development tarballs are hosted.
1011
export const developmentPublishRootUrl = "https://codeoverflow-org.github.io/nodecg-io-publish/";
@@ -54,7 +55,7 @@ async function genDependencies(opts: GenerationOptions, serviceDeps: Dependency[
5455

5556
if (opts.language === "typescript") {
5657
// For typescript we need core, all services (for typings) and special packages like ts itself or node typings.
57-
const deps = [core, ...serviceDeps, ...(await genTypeScriptDependencies(opts))];
58+
const deps = [core, ...serviceDeps, ...(await genTypeScriptDependencies(opts, install))];
5859
deps.sort();
5960
return deps;
6061
} else {
@@ -66,22 +67,30 @@ async function genDependencies(opts: GenerationOptions, serviceDeps: Dependency[
6667
/**
6768
* Generates all extra dependencies that are needed when having a bundle in TS. Meaning typescript itself, nodecg for typings
6869
* and types for node.
69-
* @param nodecgDir the directory in which nodecg is installed. Used to get nodecg version which will be used by nodecg dependency.
70+
* @return the dependencies that are needed for a TS bundle.
7071
*/
71-
async function genTypeScriptDependencies(opts: GenerationOptions): Promise<Dependency[]> {
72-
logger.debug(
73-
`Fetching latest ${opts.nodeCGTypingsPackage}, nodecg-io-tsconfig, typescript and @types/node versions...`,
74-
);
75-
const [nodecgVersion, latestTsConfig, latestTypeScript, latestNodeTypes] = await Promise.all([
76-
getLatestPackageVersion(opts.nodeCGTypingsPackage),
72+
async function genTypeScriptDependencies(opts: GenerationOptions, install: Installation): Promise<Dependency[]> {
73+
const nodecgTypingPackage = determineNodeCGImportPath(opts, install).replace("/types/server", "");
74+
if (!nodecgTypingPackage) {
75+
throw new Error("Could not determine nodecg typing package");
76+
}
77+
78+
let nodecgTypingVersion = opts.nodeCGVersion;
79+
if (nodecgTypingPackage === "nodecg-types") {
80+
logger.debug(`Fetching latest nodecg-types version...`);
81+
nodecgTypingVersion = await getLatestPackageVersion("nodecg-types");
82+
}
83+
84+
logger.debug(`Fetching latest nodecg-io-tsconfig, typescript and @types/node versions...`);
85+
const [latestTsConfig, latestTypeScript, latestNodeTypes] = await Promise.all([
7786
getLatestPackageVersion("nodecg-io-tsconfig"),
7887
getLatestPackageVersion("typescript"),
7988
getLatestPackageVersion("@types/node"),
8089
]);
8190

8291
return [
8392
["@types/node", `^${latestNodeTypes}`],
84-
[opts.nodeCGTypingsPackage, `^${nodecgVersion}`],
93+
[nodecgTypingPackage, `^${nodecgTypingVersion}`],
8594
["nodecg-io-tsconfig", `^${latestTsConfig}`],
8695
["typescript", `^${latestTypeScript}`],
8796
];

src/generate/prompt.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { getServicesFromInstall } from "../install/prompt";
77
import { yellowInstallCommand } from "./utils";
88
import { findNpmPackages, NpmPackage } from "../utils/npm";
99
import { corePackage } from "../nodecgIOVersions";
10-
import { getNodeCGIODirectory } from "../utils/nodecgInstallation";
10+
import { getNodeCGIODirectory, getNodeCGVersion } from "../utils/nodecgInstallation";
1111

1212
/**
1313
* Describes all options for bundle generation a user has answered with inside the inquirer prompt
@@ -29,7 +29,7 @@ export interface PromptedGenerationOptions {
2929
export interface GenerationOptions extends PromptedGenerationOptions {
3030
servicePackages: NpmPackage[];
3131
corePackage: NpmPackage;
32-
nodeCGTypingsPackage: "nodecg" | "nodecg-types";
32+
nodeCGVersion: semver.SemVer;
3333
bundlePath: string;
3434
}
3535

@@ -106,7 +106,7 @@ export async function promptGenerationOpts(nodecgDir: string, install: Installat
106106
},
107107
]);
108108

109-
return computeGenOptsFields(opts, install, installedPackages);
109+
return await computeGenOptsFields(opts, installedPackages, nodecgDir);
110110
}
111111

112112
// region prompt validation
@@ -156,11 +156,11 @@ function validateServiceSelection(services: string[]): true | string {
156156
* @param install the current nodecg-io installation. Used to get installed packages/exact versions.
157157
* @return opts including computed fields.
158158
*/
159-
export function computeGenOptsFields(
159+
export async function computeGenOptsFields(
160160
opts: PromptedGenerationOptions,
161-
install: Installation,
162161
installedPackages: NpmPackage[],
163-
): GenerationOptions {
162+
nodecgDir: string,
163+
): Promise<GenerationOptions> {
164164
const corePkg = installedPackages.find((pkg) => pkg.name === corePackage);
165165
if (corePkg === undefined) {
166166
throw new Error("Core package in installation info could not be found.");
@@ -178,6 +178,6 @@ export function computeGenOptsFields(
178178
return svcPackage;
179179
}),
180180
bundlePath: path.join(opts.bundleDir, opts.bundleName),
181-
nodeCGTypingsPackage: install.version === "0.1" ? "nodecg" : "nodecg-types",
181+
nodeCGVersion: await getNodeCGVersion(nodecgDir),
182182
};
183183
}

src/install/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ async function install(opts: InstallCommandOptions): Promise<void> {
9191
requestedInstall,
9292
currentInstall && !currentInstall.dev ? currentInstall : undefined,
9393
nodecgIODir,
94+
nodecgDir,
9495
);
9596
}
9697

src/install/production.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,17 @@ import { logger } from "../utils/log";
1414
import { promises as fs } from "fs";
1515
import path = require("path");
1616
import chalk = require("chalk");
17+
import { SemVer } from "semver";
18+
import { getNodeCGVersion } from "../utils/nodecgInstallation";
1719

1820
export async function createProductionInstall(
1921
requested: ProductionInstallation,
2022
current: ProductionInstallation | undefined,
2123
nodecgIODir: string,
24+
nodecgDir: string,
2225
): Promise<void> {
26+
const nodecgVersion = await getNodeCGVersion(nodecgDir);
27+
await checkNodeCGCompatibility(requested, nodecgVersion);
2328
await ensureDirectory(nodecgIODir);
2429

2530
if (current) {
@@ -41,6 +46,27 @@ export async function createProductionInstall(
4146
}
4247
}
4348

49+
/**
50+
* Checks whether the nodecg-io version that should be installed is compatible with the used NodeCG version-
51+
* @param requestedInstall the target nodecg-io install
52+
* @param nodecgDir the directory in which NodeCG is installed, used to get the NodeCG version
53+
*/
54+
export async function checkNodeCGCompatibility(requestedInstall: ProductionInstallation, nodecgVersion: SemVer) {
55+
const supportedNodeCGVersions = [1];
56+
if (requestedInstall.version !== "0.1" && requestedInstall.version !== "0.2") {
57+
// All versions above 0.2 support NodeCG v2 as well
58+
supportedNodeCGVersions.push(2);
59+
}
60+
61+
const nodeCGMajorVersion = nodecgVersion.major;
62+
if (!supportedNodeCGVersions.includes(nodeCGMajorVersion)) {
63+
throw new Error(
64+
`Your currently used NodeCG v${nodeCGMajorVersion} is not supported by nodecg-io ${requestedInstall.version}. ` +
65+
`Supported NodeCG major versions for this nodecg-io version are: ${supportedNodeCGVersions.join(", ")}`,
66+
);
67+
}
68+
}
69+
4470
/**
4571
* Finds the removed or added packages between two array of packages (current and requested install).
4672
* That way we only need to remove what is no longer requested and install what was not already present.

0 commit comments

Comments
 (0)