Skip to content
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

CLI: Migrate CLI to TypeScript #10802

Merged
merged 41 commits into from
May 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
f0954f6
refactor(cli): add `tsconfig.json`
gaetanmaisse May 14, 2020
211acb6
refactor(cli): migrate Yarn and NPM related files to TS
gaetanmaisse May 14, 2020
a4d5135
refactor(cli): migrate project_types and its tests to TS
gaetanmaisse May 14, 2020
924686a
refactor(cli): migrate helpers and its tests to TS
gaetanmaisse May 14, 2020
f923d9a
refactor(cli): migrate `add` file and its tests to TS
gaetanmaisse May 14, 2020
e5c3998
refactor(cli): migrate `detect` file and its tests to TS
gaetanmaisse May 14, 2020
ce1a495
refactor(cli): migrate `extract` file to TS
gaetanmaisse May 14, 2020
d8e77a1
refactor(cli): migrate `migrate` file to TS
gaetanmaisse May 14, 2020
9e49f81
refactor(cli): move NpmOptions and PackageJson in their own files
gaetanmaisse May 14, 2020
8e0e02f
refactor(cli): migrate Angular generator to TS
gaetanmaisse May 14, 2020
7661068
refactor(cli): migrate Aurelia generator to TS
gaetanmaisse May 14, 2020
f481ef0
refactor(cli): migrate Ember generator to TS
gaetanmaisse May 14, 2020
db5a8de
refactor(cli): migrate HTML generator to TS
gaetanmaisse May 14, 2020
eea1a03
refactor(cli): migrate Marionette generator to TS
gaetanmaisse May 14, 2020
9e0fa87
refactor(cli): migrate Marko generator to TS
gaetanmaisse May 14, 2020
a124364
refactor(cli): migrate Meteor generator to TS
gaetanmaisse May 14, 2020
493ab0f
refactor(cli): migrate Mithril generator to TS
gaetanmaisse May 14, 2020
d3179de
refactor(cli): migrate Preact generator to TS
gaetanmaisse May 14, 2020
ccdffd2
refactor(cli): migrate Rax generator to TS
gaetanmaisse May 14, 2020
30c09dd
refactor(cli): migrate React generator to TS
gaetanmaisse May 14, 2020
d4d6149
refactor(cli): migrate React Native generator to TS
gaetanmaisse May 14, 2020
97bb8fd
refactor(cli): migrate React Scripts generator to TS
gaetanmaisse May 14, 2020
7a75fd3
refactor(cli): migrate Riot generator to TS
gaetanmaisse May 14, 2020
9bb9c69
refactor(cli): migrate Sfc Vue generator to TS
gaetanmaisse May 14, 2020
828f562
refactor(cli): migrate Svelte generator to TS
gaetanmaisse May 14, 2020
ef30988
refactor(cli): migrate Vue generator to TS
gaetanmaisse May 14, 2020
d0b0d1f
refactor(cli): migrate Web Components generator to TS
gaetanmaisse May 14, 2020
306125f
refactor(cli): migrate Webpack React generator to TS
gaetanmaisse May 14, 2020
56930ac
refactor(cli): migrate update package organization generator to TS
gaetanmaisse May 15, 2020
66e077b
refactor(cli): migrate `initiate` file to TS
gaetanmaisse May 15, 2020
0cd9cde
refactor(cli): migrate `generate` file to TS
gaetanmaisse May 15, 2020
d4b2203
refactor(cli): activate strict mode in TS (except `strictNullChecks`)
gaetanmaisse May 15, 2020
66e6fdc
refactor(cli): improve typings for project types
yannbf May 16, 2020
5f4eee1
refactor(cli): improve typings for story format
yannbf May 16, 2020
d1ffb88
refactor(cli): improve typings for supported language
yannbf May 16, 2020
7ea8f77
refactor(cli): add storyformat to command options
yannbf May 17, 2020
bc237c7
test(cli): fix types by casting storyformat
yannbf May 17, 2020
cb149f6
refactor(cli): use storyformat in aurelia generator
yannbf May 17, 2020
ffa1522
refactor(cli): use a unified type across all CLI generators
gaetanmaisse May 17, 2020
5860974
refactor(cli): improve typing of `window`
gaetanmaisse May 17, 2020
9583c02
Merge branch 'next' into tech/migrate-cli-to-TS
gaetanmaisse May 17, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion lib/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,13 @@
"@storybook/svelte": "6.0.0-beta.8",
"@storybook/ui": "6.0.0-beta.8",
"@storybook/vue": "6.0.0-beta.8",
"@storybook/web-components": "6.0.0-beta.8"
"@storybook/web-components": "6.0.0-beta.8",
"@types/cross-spawn": "^6.0.1",
"@types/inquirer": "^6.5.0",
"@types/puppeteer-core": "^2.0.0",
"@types/semver": "^7.2.0",
"@types/shelljs": "^0.8.7",
"@types/update-notifier": "^0.0.30"
},
"peerDependencies": {
"jest": "*"
Expand Down
5 changes: 5 additions & 0 deletions lib/cli/src/NpmOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type NpmOptions = {
useYarn: boolean;
skipInstall?: boolean;
installAsDevDependencies?: boolean;
};
5 changes: 5 additions & 0 deletions lib/cli/src/PackageJson.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type PackageJson = {
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
peerDependencies?: Record<string, string>;
};
File renamed without changes.
39 changes: 30 additions & 9 deletions lib/cli/src/add.js → lib/cli/src/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ import { sync as spawnSync } from 'cross-spawn';
import { hasYarn } from './has_yarn';
import { latestVersion } from './latest_version';
import { commandLog, getPackageJson } from './helpers';
import { PackageJson } from './PackageJson';

const logger = console;
export const storybookAddonScope = '@storybook/addon-';

const isAddon = async (name, npmOptions) => {
const isAddon = async (
name: string,
npmOptions: {
useYarn: boolean;
}
) => {
try {
await latestVersion(npmOptions, name);
return true;
Expand All @@ -17,19 +23,23 @@ const isAddon = async (name, npmOptions) => {
}
};

const isStorybookAddon = async (name, npmOptions) =>
const isStorybookAddon = async (name: string, npmOptions: { useYarn: boolean }) =>
isAddon(`${storybookAddonScope}${name}`, npmOptions);

export const getPackageName = (addonName, isOfficialAddon) =>
export const getPackageName = (addonName: string, isOfficialAddon: boolean) =>
isOfficialAddon ? storybookAddonScope + addonName : addonName;

export const getInstalledStorybookVersion = (packageJson) =>
export const getInstalledStorybookVersion = (packageJson: PackageJson) =>
packageJson.devDependencies[
// This only considers the first occurrence.
Object.keys(packageJson.devDependencies).find((devDep) => /@storybook/.test(devDep))
] || false;

export const getPackageArg = (addonName, isOfficialAddon, packageJson) => {
export const getPackageArg = (
addonName: string,
isOfficialAddon: boolean,
packageJson: PackageJson
) => {
if (isOfficialAddon) {
const addonNameNoTag = addonName.split('@')[0];
const installedStorybookVersion = getInstalledStorybookVersion(packageJson);
Expand All @@ -40,7 +50,11 @@ export const getPackageArg = (addonName, isOfficialAddon, packageJson) => {
return addonName;
};

const installAddon = (addonName, npmOptions, isOfficialAddon) => {
const installAddon = (
addonName: string,
npmOptions: { useYarn: boolean },
isOfficialAddon: boolean
) => {
const prepareDone = commandLog(`Preparing to install the ${addonName} Storybook addon`);
prepareDone();
logger.log();
Expand Down Expand Up @@ -69,7 +83,11 @@ const installAddon = (addonName, npmOptions, isOfficialAddon) => {
installDone();
};

export const addStorybookAddonToFile = (addonName, addonsFile, isOfficialAddon) => {
export const addStorybookAddonToFile = (
addonName: string,
addonsFile: string[],
isOfficialAddon: boolean
) => {
const addonNameNoTag = addonName.split('@')[0];
const alreadyRegistered = addonsFile.find((line) => line.includes(`${addonNameNoTag}/register`));

Expand All @@ -92,7 +110,7 @@ export const addStorybookAddonToFile = (addonName, addonsFile, isOfficialAddon)

const LEGACY_CONFIGS = ['addons', 'config', 'presets'];

const postinstallAddon = async (addonName, isOfficialAddon) => {
const postinstallAddon = async (addonName: string, isOfficialAddon: boolean) => {
let skipMsg = null;
if (!isOfficialAddon) {
skipMsg = 'unofficial addon';
Expand Down Expand Up @@ -128,7 +146,10 @@ const postinstallAddon = async (addonName, isOfficialAddon) => {
}
};

export async function add(addonName, options) {
export async function add(
addonName: string,
options: { useNpm: boolean; skipPostinstall: boolean }
) {
const useYarn = Boolean(options.useNpm !== true) && hasYarn();
const npmOptions = {
useYarn,
Expand Down
68 changes: 34 additions & 34 deletions lib/cli/src/detect.test.js → lib/cli/src/detect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from 'fs';

import { getBowerJson, getPackageJson } from './helpers';
import { isStorybookInstalled, detectFrameworkPreset, detect, detectLanguage } from './detect';
import { PROJECT_TYPES, SUPPORTED_FRAMEWORKS, SUPPORTED_LANGUAGES } from './project_types';
import { ProjectType, SUPPORTED_FRAMEWORKS, SupportedLanguage } from './project_types';

jest.mock('./helpers', () => ({
getBowerJson: jest.fn(),
Expand All @@ -20,13 +20,13 @@ jest.mock('path', () => ({

const MOCK_FRAMEWORK_FILES = [
{
name: PROJECT_TYPES.METEOR,
name: ProjectType.METEOR,
files: {
'.meteor': 'file content',
},
},
{
name: PROJECT_TYPES.SFC_VUE,
name: ProjectType.SFC_VUE,
files: {
'package.json': {
dependencies: {
Expand All @@ -39,7 +39,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.VUE,
name: ProjectType.VUE,
files: {
'package.json': {
dependencies: {
Expand All @@ -49,7 +49,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.EMBER,
name: ProjectType.EMBER,
files: {
'package.json': {
devDependencies: {
Expand All @@ -59,7 +59,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.REACT_PROJECT,
name: ProjectType.REACT_PROJECT,
files: {
'package.json': {
peerDependencies: {
Expand All @@ -69,7 +69,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.REACT_NATIVE,
name: ProjectType.REACT_NATIVE,
files: {
'package.json': {
dependencies: {
Expand All @@ -82,7 +82,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.REACT_SCRIPTS,
name: ProjectType.REACT_SCRIPTS,
files: {
'package.json': {
devDependencies: {
Expand All @@ -92,7 +92,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.WEBPACK_REACT,
name: ProjectType.WEBPACK_REACT,
files: {
'package.json': {
dependencies: {
Expand All @@ -105,7 +105,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.REACT,
name: ProjectType.REACT,
files: {
'package.json': {
dependencies: {
Expand All @@ -115,7 +115,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.ANGULAR,
name: ProjectType.ANGULAR,
files: {
'package.json': {
dependencies: {
Expand All @@ -125,7 +125,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.WEB_COMPONENTS,
name: ProjectType.WEB_COMPONENTS,
files: {
'package.json': {
dependencies: {
Expand All @@ -135,7 +135,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.MITHRIL,
name: ProjectType.MITHRIL,
files: {
'package.json': {
dependencies: {
Expand All @@ -145,7 +145,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.MARIONETTE,
name: ProjectType.MARIONETTE,
files: {
'package.json': {
dependencies: {
Expand All @@ -155,7 +155,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.MARKO,
name: ProjectType.MARKO,
files: {
'package.json': {
dependencies: {
Expand All @@ -165,7 +165,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.RIOT,
name: ProjectType.RIOT,
files: {
'package.json': {
dependencies: {
Expand All @@ -175,7 +175,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.PREACT,
name: ProjectType.PREACT,
files: {
'package.json': {
dependencies: {
Expand All @@ -185,7 +185,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.SVELTE,
name: ProjectType.SVELTE,
files: {
'package.json': {
dependencies: {
Expand All @@ -195,7 +195,7 @@ const MOCK_FRAMEWORK_FILES = [
},
},
{
name: PROJECT_TYPES.RAX,
name: ProjectType.RAX,
files: {
'.rax': 'file content',
'package.json': {
Expand All @@ -209,28 +209,28 @@ const MOCK_FRAMEWORK_FILES = [

describe('Detect', () => {
it(`should return type HTML if html option is passed`, () => {
getPackageJson.mockImplementation(() => true);
expect(detect({ html: true })).toBe(PROJECT_TYPES.HTML);
(getPackageJson as jest.Mock).mockImplementation(() => true);
expect(detect({ html: true })).toBe(ProjectType.HTML);
});

it(`should return type UNDETECTED if neither packageJson or bowerJson exist`, () => {
getPackageJson.mockImplementation(() => false);
getBowerJson.mockImplementation(() => false);
expect(detect()).toBe(PROJECT_TYPES.UNDETECTED);
(getPackageJson as jest.Mock).mockImplementation(() => false);
(getBowerJson as jest.Mock).mockImplementation(() => false);
expect(detect()).toBe(ProjectType.UNDETECTED);
});

it(`should return language typescript if the dependency is present`, () => {
getPackageJson.mockImplementation(() => ({
(getPackageJson as jest.Mock).mockImplementation(() => ({
dependencies: {
typescript: '1.0.0',
},
}));
expect(detectLanguage()).toBe(SUPPORTED_LANGUAGES.TYPESCRIPT);
expect(detectLanguage()).toBe(SupportedLanguage.TYPESCRIPT);
});

it(`should return language javascript by default`, () => {
getPackageJson.mockImplementation(() => true);
expect(detectLanguage()).toBe(SUPPORTED_LANGUAGES.JAVASCRIPT);
(getPackageJson as jest.Mock).mockImplementation(() => true);
expect(detectLanguage()).toBe(SupportedLanguage.JAVASCRIPT);
});

describe('isStorybookInstalled should return', () => {
Expand Down Expand Up @@ -267,15 +267,15 @@ describe('Detect', () => {
isStorybookInstalled({
devDependencies: { '@storybook/react': '4.0.0-alpha.21' },
})
).toBe(PROJECT_TYPES.ALREADY_HAS_STORYBOOK);
).toBe(ProjectType.ALREADY_HAS_STORYBOOK);
});

it('UPDATE_PACKAGE_ORGANIZATIONS if legacy lib is detected', () => {
expect(
isStorybookInstalled({
devDependencies: { '@kadira/storybook': '4.0.0-alpha.21' },
})
).toBe(PROJECT_TYPES.UPDATE_PACKAGE_ORGANIZATIONS);
).toBe(ProjectType.UPDATE_PACKAGE_ORGANIZATIONS);
});
});

Expand All @@ -286,7 +286,7 @@ describe('Detect', () => {

MOCK_FRAMEWORK_FILES.forEach((structure) => {
it(`${structure.name}`, () => {
fs.existsSync.mockImplementation((filePath) => {
(fs.existsSync as jest.Mock).mockImplementation((filePath) => {
return Object.keys(structure.files).includes(filePath);
});

Expand All @@ -298,20 +298,20 @@ describe('Detect', () => {

it(`UNDETECTED for unknown frameworks`, () => {
const result = detectFrameworkPreset();
expect(result).toBe(PROJECT_TYPES.UNDETECTED);
expect(result).toBe(ProjectType.UNDETECTED);
});

it('REACT_SCRIPTS for custom react scripts config', () => {
const forkedReactScriptsConfig = {
'/node_modules/.bin/react-scripts': 'file content',
};

fs.existsSync.mockImplementation((filePath) => {
(fs.existsSync as jest.Mock).mockImplementation((filePath) => {
return Object.keys(forkedReactScriptsConfig).includes(filePath);
});

const result = detectFrameworkPreset();
expect(result).toBe(PROJECT_TYPES.REACT_SCRIPTS);
expect(result).toBe(ProjectType.REACT_SCRIPTS);
});
});
});