package io.j99.idea.vue.module; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.process.OSProcessHandler; import com.intellij.execution.process.ProcessOutput; import com.intellij.ide.util.projectWizard.ModuleBuilder; import com.intellij.ide.util.projectWizard.ModuleWizardStep; import com.intellij.ide.util.projectWizard.WizardContext; import com.intellij.internal.statistic.UsageTrigger; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.module.WebModuleBuilder; import com.intellij.openapi.module.WebModuleType; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; import io.j99.idea.vue.VueIcons; import io.j99.idea.vue.action.InstallAction; import io.j99.idea.vue.cli.NpmUtils; import io.j99.idea.vue.cli.nodejs.NodeRunner; import io.j99.idea.vue.component.VueProjectSettingsComponent; import io.j99.idea.vue.settings.SettingStorage; import org.jdesktop.swingx.util.OS; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.io.*; /** * Created by apple on 16/1/22. */ public class VueModuleBuilder extends ModuleBuilder { private VueProjectWizardData myWizardData; private static final Logger LOG = Logger.getInstance(VueModuleBuilder.class); void setWizardData(final VueProjectWizardData wizardData) { myWizardData = wizardData; } @Override public void setupRootModel(ModifiableRootModel modifiableRootModel) throws ConfigurationException { ContentEntry contentEntry = doAddContentEntry(modifiableRootModel); final VirtualFile baseDir = contentEntry == null ? null : contentEntry.getFile(); if (baseDir != null) { setupProject(modifiableRootModel, baseDir, myWizardData); } } protected static File createTemp() throws IOException { return FileUtil.createTempDirectory("intellij-vue-generator", null, false); } protected static void deleteTemp(File tempProject) { if (!FileUtil.delete(tempProject)) { LOG.warn("Cannot delete " + tempProject); } else { LOG.info("Successfully deleted " + tempProject); } } static void setupProject(ModifiableRootModel modifiableRootModel, final VirtualFile baseDir, VueProjectWizardData wizardData) { final String templateName = wizardData.myTemplate.getName(); Module module = modifiableRootModel.getModule(); String moduleName = module.getName(); UsageTrigger.trigger("VueProjectWizard." + templateName); ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { @Override public void run() { try { NodeRunner.ProcessListener listener = new NodeRunner.ProcessListener() { @Override public void onError(OSProcessHandler processHandler, String text) { } @Override public void onOutput(OSProcessHandler processHandler, String text) { if (text.startsWith("Project name") || text.startsWith("Project description:") || text.startsWith("Author") || text.startsWith("private")) { try { System.out.println(text); OutputStream processInput = processHandler.getProcessInput(); processInput.write(System.getProperty("line.separator").getBytes()); processInput.flush(); } catch (IOException e) { e.printStackTrace(); } } } @Override public void onCommand(OSProcessHandler processHandler, String text) { } }; ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); progressIndicator.setText("Creating..."); File tempProject = createTemp(); GeneralCommandLine cmd = NodeRunner.createCommandLine(tempProject.getPath(), wizardData.sdk.nodePath, wizardData.sdk.vuePath); cmd.addParameter("init"); cmd.addParameter(wizardData.myTemplate.getName()); cmd.addParameter(moduleName); String fullMessage = "Creating project files, please waiting ..."; String title = "Create Vue Project"; Notifications.Bus.notify( new Notification("Vue Generator", title, fullMessage, NotificationType.INFORMATION) ); ProcessOutput out = NodeRunner.execute(cmd, listener, NodeRunner.TIME_OUT); if (out.getExitCode() == 0) { setNodeAndVue(modifiableRootModel, wizardData); File[] array = tempProject.listFiles(); if (array != null && array.length != 0) { File from = ContainerUtil.getFirstItem(ContainerUtil.newArrayList(array)); assert from != null; FileUtil.copyDir(from, new File(baseDir.getPath())); deleteTemp(tempProject); install(baseDir, module, wizardData); //使用vue init创建app之后可能需要对创建好的文件进行修改 // wizardData.myTemplate.generateProject(wizardData, module, baseDir); } } else { UsageTrigger.trigger(out.getStderr()); showErrorMessage(out.getStderr()); } } catch (IOException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }, "Create Project", false, module.getProject()); } private static void showErrorMessage(@NotNull String message) { String fullMessage = "Error creating vue-loader App. " + message; String title = "Create Vue Project"; Notifications.Bus.notify( new Notification("Vue Generator", title, fullMessage, NotificationType.ERROR) ); } private static void install(VirtualFile cwd, Module module, VueProjectWizardData data) { ProgressManager.getInstance().run(new Task.Backgroundable(module.getProject(), "Install Dependencies", false) { @Override public void run(@NotNull ProgressIndicator progressIndicator) { VirtualFile baseDir = module.getProject().getBaseDir(); BufferedInputStream in = null; BufferedReader inBr = null; try { final Process process; if (OS.isWindows()) { process = Runtime.getRuntime().exec("where npm"); } else { process = Runtime.getRuntime().exec("which npm"); } in = new BufferedInputStream(process.getInputStream()); inBr = new BufferedReader(new InputStreamReader(in)); if (process.waitFor() == 0) { String npmExe = inBr.readLine(); if (StringUtil.isNotEmpty(npmExe)) { NpmUtils.packageInstall(progressIndicator,baseDir.getPath(), data.sdk.nodePath, npmExe); } else { VueProjectSettingsComponent.showNotification("please install npm!", NotificationType.WARNING); } } } catch (IOException | InterruptedException e) { e.printStackTrace(); } finally { if (inBr != null) try { inBr.close(); } catch (IOException e) { e.printStackTrace(); } if (in != null) try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } }); } static void setNodeAndVue(ModifiableRootModel modifiableRootModel, VueProjectWizardData wizardData) { saveSettings(wizardData.sdk); } protected static void saveSettings(VueProjectWizardData.Sdk sdk) { SettingStorage settingStorage = SettingStorage.getInstance(); settingStorage.vueExePath = sdk.vuePath; settingStorage.nodeInterpreter = sdk.nodePath; } protected VueProjectWizardData.Sdk loadSettings() { SettingStorage settingStorage = SettingStorage.getInstance(); return new VueProjectWizardData.Sdk(settingStorage.nodeInterpreter, settingStorage.vueExePath); } @Override public String getParentGroup() { return WebModuleBuilder.GROUP_NAME; } @Override public Icon getNodeIcon() { return VueIcons.VUE_ICON; } @Override public Icon getBigIcon() { return VueIcons.VUE_ICON; } @Override public String getName() { return "Vue"; } @Override public String getPresentableName() { return "Vue"; } @Nullable @Override public ModuleWizardStep getCustomOptionsStep(WizardContext context, Disposable parentDisposable) { VueModuleWizardStep step = new VueModuleWizardStep(context, loadSettings()); Disposer.register(parentDisposable, step); return step; } @Override public ModuleType getModuleType() { return WebModuleType.getInstance(); } static void runWhenNonModalIfModuleNotDisposed(@NotNull final Runnable runnable, @NotNull final Module module) { StartupManager.getInstance(module.getProject()).runWhenProjectIsInitialized(new Runnable() { @Override public void run() { if (ApplicationManager.getApplication().getCurrentModalityState() == ModalityState.NON_MODAL) { runnable.run(); } else { ApplicationManager.getApplication().invokeLater(runnable, ModalityState.NON_MODAL, new Condition() { @Override public boolean value(final Object o) { return module.isDisposed(); } }); } } }); } }