-
Notifications
You must be signed in to change notification settings - Fork 10.3k
/
Copy pathgatsby-theme-eject.js
executable file
·126 lines (113 loc) · 3.8 KB
/
gatsby-theme-eject.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env node
"use strict";
const fs = require("fs");
const path = require("path");
const glob = require("glob");
const mkdirp = require("mkdirp");
const { sortBy, uniq } = require("lodash");
const babel = require("@babel/core");
const declare = require("@babel/helper-plugin-utils").declare;
const loadThemes = require("gatsby/dist/bootstrap/load-themes");
var inquirer = require("inquirer");
inquirer
.prompt([
{
type: "list",
name: "theme",
message: "What theme are you trying to eject from?",
choices: async () => {
const config = require(path.join(
process.cwd(),
"gatsby-config.js"
), "utf-8");
const configWithThemes = await loadThemes(config, {
useLegacyThemes: false
});
return sortBy(
uniq(configWithThemes.config.plugins.map(({ resolve }) => resolve))
);
}
},
{
type: "list",
name: "component",
message: "Select the file you would like to shadow:",
choices: ({ theme }) => {
const themeSrc = path.join(path.dirname(require.resolve(theme)), `src`);
return glob.sync(`**/*`, {
cwd: themeSrc
});
}
},
{
type: "list",
name: "shadowType",
message: "Would you like to copy or extend?",
choices: [`copy`, `extend`]
}
])
.then(({ theme, component, shadowType }) => {
const componentPath = path.dirname(component);
mkdirp.sync(path.join(process.cwd(), "src", theme, componentPath));
const finalPath = path.join(process.cwd(), "src", theme, component);
if (shadowType === "copy") {
const componentFromThemePath = path.join(
path.dirname(require.resolve(theme)),
"src",
component
);
const npmModuleComponentImport = path.join(theme, "src", component);
const { ext } = path.parse(component);
if ([".js", ".jsx"].includes(ext)) {
// apply babel relative path transformations
const instance = new BabelPluginTransformRelativeImports({
originalComponentPath: npmModuleComponentImport
});
// Read the component file in preparation for transforming relative file imports
const componentCodeString = fs.readFileSync(componentFromThemePath);
const result = babel.transform(componentCodeString, {
configFile: false,
plugins: [instance.plugin, require("@babel/plugin-syntax-jsx")]
});
fs.writeFileSync(finalPath, result.code);
} else {
// if we don't know how to process the file type, just copy it
// and let the user decide what to do
fs.copyFileSync(componentFromThemePath, finalPath);
}
} else if (shadowType === "extend") {
fs.writeFileSync(
finalPath,
`import Component from '${theme}/src/${component}';
export default props => <Component {...props}/>`
);
}
});
class BabelPluginTransformRelativeImports {
// originalComponentPath is the path from the node module to the component
constructor({ originalComponentPath }) {
this.plugin = declare(api => {
api.assertVersion(7);
return {
visitor: {
StringLiteral({ node }) {
let split = node.value.split("!");
const nodePath = split.pop();
const loaders = `${split.join("!")}${split.length > 0 ? "!" : ""}`;
if (nodePath.startsWith(".")) {
console.log(originalComponentPath, nodePath);
const valueAbsPath = path.resolve(
originalComponentPath,
nodePath
);
const replacementPath =
loaders +
path.join(path.dirname(originalComponentPath), nodePath);
node.value = replacementPath;
}
}
}
};
});
}
}