@grafana/create-plugin 5.17.0 → 5.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/CONTRIBUTING.md +117 -1
- package/dist/commands/update.command.js +28 -68
- package/dist/commands/update.migrate.command.js +27 -0
- package/dist/commands/update.standard.command.js +51 -0
- package/dist/migrations/context.js +118 -0
- package/dist/migrations/context.test.js +122 -0
- package/dist/migrations/fixtures/foo/bar.js +2 -0
- package/dist/migrations/fixtures/foo/baz.js +1 -0
- package/dist/migrations/fixtures/migrations.js +19 -0
- package/dist/migrations/manager.js +50 -0
- package/dist/migrations/manager.test.js +177 -0
- package/dist/migrations/migrations.js +3 -0
- package/dist/migrations/migrations.test.js +10 -0
- package/dist/migrations/scripts/example-migration.js +23 -0
- package/dist/migrations/scripts/example-migration.test.js +24 -0
- package/dist/migrations/test-utils.js +9 -0
- package/dist/migrations/utils.js +46 -0
- package/dist/migrations/utils.test.js +65 -0
- package/dist/utils/utils.cli.js +3 -0
- package/dist/utils/utils.config.js +9 -1
- package/dist/utils/utils.console.js +6 -0
- package/dist/utils/utils.git.js +13 -0
- package/dist/utils/utils.goSdk.js +6 -6
- package/dist/utils/utils.templates.js +3 -3
- package/package.json +6 -4
- package/src/commands/update.command.ts +33 -75
- package/src/commands/update.migrate.command.ts +31 -0
- package/src/commands/update.standard.command.ts +55 -0
- package/src/migrations/context.test.ts +148 -0
- package/src/migrations/context.ts +155 -0
- package/src/migrations/fixtures/foo/bar.ts +1 -0
- package/src/migrations/fixtures/foo/baz.ts +0 -0
- package/src/migrations/fixtures/migrations.ts +19 -0
- package/src/migrations/manager.test.ts +217 -0
- package/src/migrations/manager.ts +70 -0
- package/src/migrations/migrations.test.ts +12 -0
- package/src/migrations/migrations.ts +20 -0
- package/src/migrations/scripts/example-migration.test.ts +40 -0
- package/src/migrations/scripts/example-migration.ts +34 -0
- package/src/migrations/test-utils.ts +12 -0
- package/src/migrations/utils.test.ts +81 -0
- package/src/migrations/utils.ts +50 -0
- package/src/utils/utils.cli.ts +5 -0
- package/src/utils/utils.config.ts +12 -1
- package/src/utils/utils.console.ts +7 -0
- package/src/utils/utils.git.ts +14 -0
- package/src/utils/utils.goSdk.ts +6 -6
- package/src/utils/utils.templates.ts +3 -3
- package/templates/common/.config/docker-compose-base.yaml +1 -1
- package/templates/common/_package.json +4 -4
- package/tsconfig.json +1 -1
- package/vitest.config.ts +1 -0
- package/vitest.d.ts +11 -0
- package/vitest.setup.ts +53 -0
|
@@ -1,91 +1,49 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
1
|
import minimist from 'minimist';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { getOnlyExistingInCwd, removeFilesInCwd } from '../utils/utils.files.js';
|
|
2
|
+
import { standardUpdate } from './update.standard.command.js';
|
|
3
|
+
import { migrationUpdate } from './update.migrate.command.js';
|
|
6
4
|
import { isGitDirectory, isGitDirectoryClean } from '../utils/utils.git.js';
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import { isPluginDirectory, updateDotConfigFolder } from '../utils/utils.plugin.js';
|
|
11
|
-
import { getGrafanaRuntimeVersion, getVersion } from '../utils/utils.version.js';
|
|
5
|
+
import { printRedBox } from '../utils/utils.console.js';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { isPluginDirectory } from '../utils/utils.plugin.js';
|
|
12
8
|
|
|
13
9
|
export const update = async (argv: minimist.ParsedArgs) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
title: 'You are not inside a git directory',
|
|
19
|
-
content: `In order to proceed please run "git init" in the root of your project and commit your changes.\n
|
|
10
|
+
if (!(await isGitDirectory()) && !argv.force) {
|
|
11
|
+
printRedBox({
|
|
12
|
+
title: 'You are not inside a git directory',
|
|
13
|
+
content: `In order to proceed please run "git init" in the root of your project and commit your changes.\n
|
|
20
14
|
(This check is necessary to make sure that the updates are easy to revert and don't mess with any changes you currently have.
|
|
21
15
|
In case you want to proceed as is please use the ${chalk.bold('--force')} flag.)`,
|
|
22
|
-
|
|
16
|
+
});
|
|
23
17
|
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
26
20
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
21
|
+
if (!(await isGitDirectoryClean()) && !argv.force) {
|
|
22
|
+
printRedBox({
|
|
23
|
+
title: 'Please clean your repository working tree before updating.',
|
|
24
|
+
subtitle: '(Commit your changes or stash them.)',
|
|
25
|
+
content: `(This check is necessary to make sure that the updates are easy to revert and don't mess with any changes you currently have.
|
|
32
26
|
In case you want to proceed as is please use the ${chalk.bold('--force')} flag.)`,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (!isPluginDirectory() && !argv.force) {
|
|
39
|
-
printRedBox({
|
|
40
|
-
title: 'Are you inside a plugin directory?',
|
|
41
|
-
subtitle: 'We couldn\'t find a "src/plugin.json" file under your current directory.',
|
|
42
|
-
content: `(Please make sure to run this command from the root of your plugin folder. In case you want to proceed as is please use the ${chalk.bold(
|
|
43
|
-
'--force'
|
|
44
|
-
)} flag.)`,
|
|
45
|
-
});
|
|
27
|
+
});
|
|
46
28
|
|
|
47
|
-
|
|
48
|
-
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
49
31
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
ignoreGrafanaDependencies: false,
|
|
32
|
+
if (!isPluginDirectory() && !argv.force) {
|
|
33
|
+
printRedBox({
|
|
34
|
+
title: 'Are you inside a plugin directory?',
|
|
35
|
+
subtitle: 'We couldn\'t find a "src/plugin.json" file under your current directory.',
|
|
36
|
+
content: `(Please make sure to run this command from the root of your plugin folder. In case you want to proceed as is please use the ${chalk.bold(
|
|
37
|
+
'--force'
|
|
38
|
+
)} flag.)`,
|
|
58
39
|
});
|
|
59
|
-
await updateGoSdkAndModules(process.cwd());
|
|
60
|
-
|
|
61
|
-
const filesToRemove = getOnlyExistingInCwd(UDPATE_CONFIG.filesToRemove);
|
|
62
|
-
if (filesToRemove.length) {
|
|
63
|
-
removeFilesInCwd(filesToRemove);
|
|
64
|
-
}
|
|
65
40
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
content: `${chalk.bold('@grafana/* package version:')} ${getGrafanaRuntimeVersion()}
|
|
69
|
-
${chalk.bold('@grafana/create-plugin version:')} ${getVersion()}
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
70
43
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
- 2. Check if you encounter any breaking changes
|
|
74
|
-
(refer to our migration guide: https://grafana.com/developers/plugin-tools/migration-guides/update-from-grafana-versions/)
|
|
75
|
-
${chalk.bold('Do you have questions?')}
|
|
76
|
-
Please don't hesitate to reach out in one of the following ways:
|
|
77
|
-
- Open an issue in https://github.com/grafana/plugin-tools
|
|
78
|
-
- Ask a question in the community forum at https://community.grafana.com/c/plugin-development/30
|
|
79
|
-
- Join our community slack channel at https://slack.grafana.com/`,
|
|
80
|
-
});
|
|
81
|
-
} catch (error) {
|
|
82
|
-
if (error instanceof Error) {
|
|
83
|
-
printRedBox({
|
|
84
|
-
title: 'Something went wrong while updating your plugin.',
|
|
85
|
-
content: error.message,
|
|
86
|
-
});
|
|
87
|
-
} else {
|
|
88
|
-
console.error(error);
|
|
89
|
-
}
|
|
44
|
+
if (argv.experimentalUpdates) {
|
|
45
|
+
return await migrationUpdate(argv);
|
|
90
46
|
}
|
|
47
|
+
|
|
48
|
+
return await standardUpdate();
|
|
91
49
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import minimist from 'minimist';
|
|
2
|
+
import { gte } from 'semver';
|
|
3
|
+
import { getMigrationsToRun, runMigrations } from '../migrations/manager.js';
|
|
4
|
+
import { getConfig } from '../utils/utils.config.js';
|
|
5
|
+
import { getVersion } from '../utils/utils.version.js';
|
|
6
|
+
import { printHeader } from '../utils/utils.console.js';
|
|
7
|
+
|
|
8
|
+
export const migrationUpdate = async (argv: minimist.ParsedArgs) => {
|
|
9
|
+
try {
|
|
10
|
+
const projectCpVersion = getConfig().version;
|
|
11
|
+
const packageCpVersion = getVersion();
|
|
12
|
+
|
|
13
|
+
if (gte(projectCpVersion, packageCpVersion)) {
|
|
14
|
+
console.warn('Nothing to update, exiting.');
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
console.log(`Running migrations from ${projectCpVersion} to ${packageCpVersion}.`);
|
|
19
|
+
|
|
20
|
+
const commitEachMigration = argv.commit;
|
|
21
|
+
const migrations = getMigrationsToRun(projectCpVersion, packageCpVersion);
|
|
22
|
+
await runMigrations(migrations, { commitEachMigration });
|
|
23
|
+
printHeader('the update command completed successfully.');
|
|
24
|
+
console.log('');
|
|
25
|
+
} catch (error) {
|
|
26
|
+
printHeader('the update command encountered an error.');
|
|
27
|
+
console.log('');
|
|
28
|
+
console.error(error);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { UDPATE_CONFIG } from '../constants.js';
|
|
3
|
+
import { printBlueBox, printRedBox } from '../utils/utils.console.js';
|
|
4
|
+
import { getOnlyExistingInCwd, removeFilesInCwd } from '../utils/utils.files.js';
|
|
5
|
+
import { updateGoSdkAndModules } from '../utils/utils.goSdk.js';
|
|
6
|
+
import { updateNpmScripts, updatePackageJson } from '../utils/utils.npm.js';
|
|
7
|
+
import { getPackageManagerFromUserAgent } from '../utils/utils.packageManager.js';
|
|
8
|
+
import { updateDotConfigFolder } from '../utils/utils.plugin.js';
|
|
9
|
+
import { getGrafanaRuntimeVersion, getVersion } from '../utils/utils.version.js';
|
|
10
|
+
|
|
11
|
+
export const standardUpdate = async () => {
|
|
12
|
+
const { packageManagerName } = getPackageManagerFromUserAgent();
|
|
13
|
+
try {
|
|
14
|
+
// Updating the plugin (.config/, NPM package dependencies, package.json scripts)
|
|
15
|
+
// (More info on the why: https://docs.google.com/document/d/15dm4WV9v7Ga9Z_Hp3CJMf2D3meuTyEWqBc3omqiOksQ)
|
|
16
|
+
// -------------------
|
|
17
|
+
await updateDotConfigFolder();
|
|
18
|
+
updateNpmScripts();
|
|
19
|
+
updatePackageJson({
|
|
20
|
+
onlyOutdated: true,
|
|
21
|
+
ignoreGrafanaDependencies: false,
|
|
22
|
+
});
|
|
23
|
+
await updateGoSdkAndModules(process.cwd());
|
|
24
|
+
|
|
25
|
+
const filesToRemove = getOnlyExistingInCwd(UDPATE_CONFIG.filesToRemove);
|
|
26
|
+
if (filesToRemove.length) {
|
|
27
|
+
removeFilesInCwd(filesToRemove);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
printBlueBox({
|
|
31
|
+
title: 'Update successful ✔',
|
|
32
|
+
content: `${chalk.bold('@grafana/* package version:')} ${getGrafanaRuntimeVersion()}
|
|
33
|
+
${chalk.bold('@grafana/create-plugin version:')} ${getVersion()}
|
|
34
|
+
|
|
35
|
+
${chalk.bold.underline('Next steps:')}
|
|
36
|
+
- 1. Run ${chalk.bold(`${packageManagerName} install`)} to install the package updates
|
|
37
|
+
- 2. Check if you encounter any breaking changes
|
|
38
|
+
(refer to our migration guide: https://grafana.com/developers/plugin-tools/migration-guides/update-from-grafana-versions/)
|
|
39
|
+
${chalk.bold('Do you have questions?')}
|
|
40
|
+
Please don't hesitate to reach out in one of the following ways:
|
|
41
|
+
- Open an issue in https://github.com/grafana/plugin-tools
|
|
42
|
+
- Ask a question in the community forum at https://community.grafana.com/c/plugin-development/30
|
|
43
|
+
- Join our community slack channel at https://slack.grafana.com/`,
|
|
44
|
+
});
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (error instanceof Error) {
|
|
47
|
+
printRedBox({
|
|
48
|
+
title: 'Something went wrong while updating your plugin.',
|
|
49
|
+
content: error.message,
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
console.error(error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { Context } from './context.js';
|
|
2
|
+
|
|
3
|
+
describe('Context', () => {
|
|
4
|
+
describe('getFile', () => {
|
|
5
|
+
it('should read a file from the file system', () => {
|
|
6
|
+
const context = new Context(`${__dirname}/fixtures`);
|
|
7
|
+
const content = context.getFile('foo/bar.ts');
|
|
8
|
+
expect(content).toEqual("console.log('foo/bar.ts');\n");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should get a file that was just added to the context', () => {
|
|
12
|
+
const context = new Context();
|
|
13
|
+
context.addFile('file.txt', 'content');
|
|
14
|
+
const content = context.getFile('file.txt');
|
|
15
|
+
expect(content).toEqual('content');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should get a file that was updated in the current context', () => {
|
|
19
|
+
const context = new Context(`${__dirname}/fixtures`);
|
|
20
|
+
context.updateFile('foo/bar.ts', 'content');
|
|
21
|
+
const content = context.getFile('foo/bar.ts');
|
|
22
|
+
expect(content).toEqual('content');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should not return a file that was marked for deletion', () => {
|
|
26
|
+
const context = new Context(`${__dirname}/fixtures`);
|
|
27
|
+
context.deleteFile('foo/bar.ts');
|
|
28
|
+
const content = context.getFile('foo/bar.ts');
|
|
29
|
+
expect(content).toEqual(undefined);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('addFile', () => {
|
|
34
|
+
it('should add a file to the context', () => {
|
|
35
|
+
const context = new Context();
|
|
36
|
+
context.addFile('file.txt', 'content');
|
|
37
|
+
expect(context.listChanges()).toEqual({ 'file.txt': { content: 'content', changeType: 'add' } });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should not add a file if it already exists', () => {
|
|
41
|
+
const context = new Context();
|
|
42
|
+
context.addFile('file.txt', 'content');
|
|
43
|
+
|
|
44
|
+
expect(() => context.addFile('file.txt', 'new content')).toThrowError('File file.txt already exists');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('deleteFile', () => {
|
|
49
|
+
it('should delete a file from the context', () => {
|
|
50
|
+
const context = new Context();
|
|
51
|
+
context.addFile('file.txt', 'content');
|
|
52
|
+
context.deleteFile('file.txt');
|
|
53
|
+
expect(context.listChanges()).toEqual({});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should not delete a file if it does not exist', () => {
|
|
57
|
+
const context = new Context();
|
|
58
|
+
|
|
59
|
+
expect(() => context.deleteFile('file.txt')).toThrowError('File file.txt does not exist');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('updateFile', () => {
|
|
64
|
+
it('should update a file in the context', () => {
|
|
65
|
+
const context = new Context();
|
|
66
|
+
context.addFile('file.txt', 'content');
|
|
67
|
+
context.updateFile('file.txt', 'new content');
|
|
68
|
+
expect(context.listChanges()).toEqual({ 'file.txt': { content: 'new content', changeType: 'update' } });
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should not update a file if it does not exist', () => {
|
|
72
|
+
const context = new Context();
|
|
73
|
+
|
|
74
|
+
expect(() => context.updateFile('file.txt', 'new content')).toThrowError('File file.txt does not exist');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('renameFile', () => {
|
|
79
|
+
it('should rename a file', () => {
|
|
80
|
+
const context = new Context(`${__dirname}/fixtures`);
|
|
81
|
+
context.renameFile('foo/bar.ts', 'new-file.txt');
|
|
82
|
+
expect(context.listChanges()).toEqual({
|
|
83
|
+
'new-file.txt': { content: "console.log('foo/bar.ts');\n", changeType: 'add' },
|
|
84
|
+
'foo/bar.ts': { changeType: 'delete' },
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should not rename a file if it does not exist', () => {
|
|
89
|
+
const context = new Context();
|
|
90
|
+
|
|
91
|
+
expect(() => context.renameFile('file.txt', 'new-file.txt')).toThrowError('File file.txt does not exist');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should not rename a file if the new name already exists', () => {
|
|
95
|
+
const context = new Context();
|
|
96
|
+
context.addFile('file.txt', 'content');
|
|
97
|
+
context.addFile('new-file.txt', 'content');
|
|
98
|
+
|
|
99
|
+
expect(() => context.renameFile('file.txt', 'new-file.txt')).toThrowError('File new-file.txt already exists');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('readDir', () => {
|
|
104
|
+
it('should read the directory', () => {
|
|
105
|
+
const context = new Context(`${__dirname}/fixtures`);
|
|
106
|
+
const files = context.readDir('foo');
|
|
107
|
+
expect(files).toEqual(['foo/bar.ts', 'foo/baz.ts']);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should filter out deleted files', () => {
|
|
111
|
+
const context = new Context(`${__dirname}/fixtures`);
|
|
112
|
+
context.deleteFile('foo/bar.ts');
|
|
113
|
+
const files = context.readDir('foo');
|
|
114
|
+
expect(files).toEqual(['foo/baz.ts']);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should include files that are only added to the context', () => {
|
|
118
|
+
const context = new Context(`${__dirname}/fixtures`);
|
|
119
|
+
context.addFile('foo/foo.txt', '');
|
|
120
|
+
const files = context.readDir('foo');
|
|
121
|
+
expect(files).toEqual(['foo/bar.ts', 'foo/baz.ts', 'foo/foo.txt']);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('normalisePath', () => {
|
|
126
|
+
it('should normalise the path', () => {
|
|
127
|
+
const context = new Context(`${__dirname}/fixtures`);
|
|
128
|
+
expect(context.normalisePath('foo/bar.ts')).toEqual('foo/bar.ts');
|
|
129
|
+
expect(context.normalisePath('./foo/bar.ts')).toEqual('foo/bar.ts');
|
|
130
|
+
expect(context.normalisePath('/foo/bar.ts')).toEqual('foo/bar.ts');
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('hasChanges', () => {
|
|
135
|
+
it('should return FALSE if the context has no changes', () => {
|
|
136
|
+
const context = new Context(`${__dirname}/fixtures`);
|
|
137
|
+
expect(context.hasChanges()).toEqual(false);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should return TRUE if the context has changes', () => {
|
|
141
|
+
const context = new Context(`${__dirname}/fixtures`);
|
|
142
|
+
|
|
143
|
+
context.addFile('foo.ts', '');
|
|
144
|
+
|
|
145
|
+
expect(context.hasChanges()).toEqual(true);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { constants, accessSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { relative, normalize, join, dirname } from 'node:path';
|
|
3
|
+
import { migrationsDebug } from './utils.js';
|
|
4
|
+
|
|
5
|
+
export type ContextFile = Record<
|
|
6
|
+
string,
|
|
7
|
+
{
|
|
8
|
+
content?: string;
|
|
9
|
+
changeType: 'add' | 'delete' | 'update';
|
|
10
|
+
}
|
|
11
|
+
>;
|
|
12
|
+
|
|
13
|
+
export class Context {
|
|
14
|
+
private files: ContextFile = {};
|
|
15
|
+
basePath: string;
|
|
16
|
+
|
|
17
|
+
constructor(basePath?: string) {
|
|
18
|
+
this.basePath = basePath || process.cwd();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
addFile(filePath: string, content: string) {
|
|
22
|
+
const path = this.normalisePath(filePath);
|
|
23
|
+
if (!this.doesFileExist(path)) {
|
|
24
|
+
this.files[path] = { content, changeType: 'add' };
|
|
25
|
+
} else {
|
|
26
|
+
throw new Error(`File ${path} already exists`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
deleteFile(filePath: string) {
|
|
31
|
+
const path = this.normalisePath(filePath);
|
|
32
|
+
|
|
33
|
+
// Delete a file that was added to the current context
|
|
34
|
+
if (this.files[path] && this.files[path].changeType === 'add') {
|
|
35
|
+
delete this.files[path];
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// Delete a file from the disk
|
|
39
|
+
else if (this.doesFileExistOnDisk(path)) {
|
|
40
|
+
this.files[path] = { ...this.files[path], changeType: 'delete' };
|
|
41
|
+
}
|
|
42
|
+
// Delete a file that was updated in the current context
|
|
43
|
+
else if (this.files[path] && this.files[path].changeType === 'update') {
|
|
44
|
+
throw new Error(`File ${path} was marked as updated already`);
|
|
45
|
+
} else {
|
|
46
|
+
throw new Error(`File ${path} does not exist`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
updateFile(filePath: string, content: string) {
|
|
51
|
+
const path = this.normalisePath(filePath);
|
|
52
|
+
const originalContent = this.getFile(path);
|
|
53
|
+
|
|
54
|
+
if (originalContent === undefined) {
|
|
55
|
+
throw new Error(`File ${path} does not exist`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (originalContent !== content) {
|
|
59
|
+
this.files[path] = { content, changeType: 'update' };
|
|
60
|
+
} else {
|
|
61
|
+
migrationsDebug(`Context.updateFile() - no updates for ${filePath}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
doesFileExist(filePath: string) {
|
|
66
|
+
const path = this.normalisePath(filePath);
|
|
67
|
+
|
|
68
|
+
// Added / updated in this context
|
|
69
|
+
if (this.files[path] && this.files[path].changeType !== 'delete') {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return this.doesFileExistOnDisk(path);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
doesFileExistOnDisk(filePath: string) {
|
|
77
|
+
const path = join(this.basePath, this.normalisePath(filePath));
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
accessSync(path, constants.R_OK | constants.W_OK);
|
|
81
|
+
return true;
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getFile(filePath: string) {
|
|
88
|
+
const path = this.normalisePath(filePath);
|
|
89
|
+
|
|
90
|
+
// Deleted in this context
|
|
91
|
+
if (this.files[path] && this.files[path].changeType === 'delete') {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Added / updated in this context
|
|
96
|
+
if (this.files[path]) {
|
|
97
|
+
return this.files[path].content;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (this.doesFileExistOnDisk(path)) {
|
|
101
|
+
return readFileSync(join(this.basePath, path), 'utf-8');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
listChanges() {
|
|
108
|
+
return this.files;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
hasChanges() {
|
|
112
|
+
return Object.keys(this.files).length > 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
renameFile(from: string, to: string) {
|
|
116
|
+
const normalisedTo = this.normalisePath(to);
|
|
117
|
+
const contents = this.getFile(from);
|
|
118
|
+
|
|
119
|
+
if (contents === undefined) {
|
|
120
|
+
throw new Error(`File ${from} does not exist`);
|
|
121
|
+
}
|
|
122
|
+
// File was already touched in this context
|
|
123
|
+
else if (this.files[normalisedTo]) {
|
|
124
|
+
throw new Error(`File ${to} already exists`);
|
|
125
|
+
} else {
|
|
126
|
+
this.deleteFile(from);
|
|
127
|
+
this.addFile(to, contents);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
readDir(folderPath: string): string[] {
|
|
132
|
+
const path = this.normalisePath(folderPath);
|
|
133
|
+
const childrenOnDisk = this.readDirFromDisk(folderPath)
|
|
134
|
+
.map((child) => join(path, child))
|
|
135
|
+
.filter((child) => !this.files[child] || this.files[child].changeType !== 'delete');
|
|
136
|
+
const childrenAddedInContext = Object.keys(this.files).filter(
|
|
137
|
+
(p) => dirname(p) === path && this.files[p].changeType === 'add'
|
|
138
|
+
);
|
|
139
|
+
return [...childrenOnDisk, ...childrenAddedInContext];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
readDirFromDisk(folderPath: string): string[] {
|
|
143
|
+
const path = this.normalisePath(folderPath);
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
return readdirSync(join(this.basePath, path));
|
|
147
|
+
} catch (error) {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
normalisePath(filePath: string) {
|
|
153
|
+
return normalize(relative(this.basePath, join(this.basePath, filePath)));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
console.log('foo/bar.ts');
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
migrations: {
|
|
3
|
+
'migration-key1': {
|
|
4
|
+
version: '5.0.0',
|
|
5
|
+
description: 'Update project to use new cache directory',
|
|
6
|
+
migrationScript: './5-0-0-cache-directory.js',
|
|
7
|
+
},
|
|
8
|
+
'migration-key2': {
|
|
9
|
+
version: '5.4.0',
|
|
10
|
+
description: 'Update project to use new cache directory',
|
|
11
|
+
migrationScript: './5-4-0-cache-directory.js',
|
|
12
|
+
},
|
|
13
|
+
'migration-key3': {
|
|
14
|
+
version: '6.0.0',
|
|
15
|
+
description: 'Update project to use new cache directory',
|
|
16
|
+
migrationScript: './5-4-0-cache-directory.js',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
};
|