serverless/lib/cli/triage.js
Mariusz Nowak 4494f77f61 fix(CLI): Fix component template recognition in triage
Top level configuration not containing `component` was considered unconditionally as `serverless`, that's not correct
2021-11-17 13:43:23 +01:00

193 lines
6.2 KiB
JavaScript

'use strict';
module.exports = async () => {
const cliArgs = new Set(process.argv.slice(2));
// Unconditionally favor "serverless" for version check
if (cliArgs.has('--version')) return 'serverless';
if (cliArgs.has('-v')) return 'serverless';
// Unconditionally favor "@serverless/components" when component specific command or flag
const componentsCommands = new Set(['registry', 'init', 'publish']);
if (componentsCommands.has(process.argv[2])) return '@serverless/components';
if (cliArgs.has('--help-components')) return '@serverless/components';
if (cliArgs.has('--target')) return '@serverless/components';
// Detect eventual component configuration
const fsp = require('fs').promises;
if (
(
await Promise.all(
['yml', 'yaml', 'json'].map(async (extension) => {
try {
await fsp.access(`serverless.component.${extension}`);
return true;
} catch {
return false;
}
})
)
).some(Boolean)
) {
return '@serverless/components';
}
// Detect eventual service configuration
const configurationExtension =
(
await Promise.all(
['yml', 'yaml', 'json', 'js', 'ts'].map(async (extension) => {
try {
await fsp.access(`serverless.${extension}`);
return extension;
} catch {
return null;
}
})
)
).find(Boolean) || null;
if (configurationExtension) {
// Found top level service configuration, recognize CLI by content
const targetCliName = await (async () => {
const resolveByObjectConfiguration = (configuration) => {
if (configuration.provider) return 'serverless';
if (configuration.component) return '@serverless/components';
for (const value of Object.values(configuration)) {
if (value.component) return '@serverless/cli';
}
return null;
};
switch (configurationExtension) {
case 'yml':
case 'yaml': {
const content = await fsp.readFile(`serverless.${configurationExtension}`, 'utf8');
if (content.search(/(?:^|\n)provider\s*:/) !== -1) return 'serverless';
if (content.search(/(?:^|\n)component\s*:/) !== -1) return '@serverless/components';
if (content.search(/\n\s+component\s*:/) !== -1) return '@serverless/cli';
return null;
}
case 'json': {
const configuration = (() => {
try {
return require(`${process.cwd()}/serverless.json`);
} catch {
return null;
}
})();
if (!configuration) return 'serverless';
return resolveByObjectConfiguration(configuration);
}
case 'js': {
const configuration = (() => {
try {
return require(`${process.cwd()}/serverless.js`);
} catch {
return null;
}
})();
if (!configuration) return 'serverless';
if (typeof configuration === 'function') return '@serverless/cli';
return resolveByObjectConfiguration(configuration);
}
case 'ts':
return 'serverless';
default:
throw new Error(`Unrecognized extension "${configurationExtension}"`);
}
})();
if (targetCliName) return targetCliName;
}
// No top level service configuration
const isChinaUser = (() => {
if (process.env.SLS_GEO_LOCATION) return process.env.SLS_GEO_LOCATION === 'cn';
return new Intl.DateTimeFormat('en', { timeZoneName: 'long' })
.format()
.includes('China Standard Time');
})();
// If "sls" or "sls deploy" command and in China, force "@serverless/components"
if (
(process.argv.length === 2 || process.argv[2] === 'deploy') &&
(isChinaUser || process.env.SERVERLESS_PLATFORM_VENDOR === 'tencent')
) {
return '@serverless/components';
}
// Detect eventual component template
const nestedTemplateKeywords = new Set([
'deploy',
'remove',
'info',
'help',
'--help',
'dev',
'logs',
'invoke',
'credentials',
]);
if (!nestedTemplateKeywords.has(process.argv[2])) return 'serverless';
const path = require('path');
const detectSubServiceType = async (subDirname) => {
const subConfigurationExtension =
(
await Promise.all(
['yml', 'yaml', 'json', 'js'].map(async (extension) => {
try {
await fsp.access(path.resolve(subDirname, `serverless.${extension}`));
return extension;
} catch {
return null;
}
})
)
).find(Boolean) || null;
if (!subConfigurationExtension) return null;
switch (subConfigurationExtension) {
case 'yml':
case 'yaml': {
const content = await fsp.readFile(
path.resolve(subDirname, `serverless.${subConfigurationExtension}`),
'utf8'
);
if (content.search(/(?:^|\n)component\s*:/) !== -1) return '@serverless/components';
return 'other';
}
case 'json': {
const configuration = (() => {
try {
return require(`${path.resolve(subDirname)}/serverless.json`);
} catch {
return null;
}
})();
return configuration && configuration.component ? '@serverless/components' : 'other';
}
case 'js': {
const configuration = (() => {
try {
return require(`${path.resolve(subDirname)}/serverless.js`);
} catch {
return null;
}
})();
return configuration && configuration.component ? '@serverless/components' : 'other';
}
default:
throw new Error(`Unrecognized extension "${subConfigurationExtension}"`);
}
};
let hasComponent = false;
for (const subDirname of await fsp.readdir('.')) {
const stats = await fsp.lstat(subDirname);
if (!stats.isDirectory()) continue;
const subServiceType = await detectSubServiceType(subDirname);
if (!subServiceType) continue;
if (subServiceType === '@serverless/components') hasComponent = true;
else return 'serverless';
}
return hasComponent ? '@serverless/components' : 'serverless';
};