All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.redhat.ceylon.cmr.impl.NpmContentStore Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
/*
 * Copyright 2011 Red Hat inc. and third party contributors as noted
 * by the author tags.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.redhat.ceylon.cmr.impl;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;

import com.redhat.ceylon.cmr.api.ArtifactContext;
import com.redhat.ceylon.cmr.resolver.javascript.JavaScriptResolver;
import com.redhat.ceylon.cmr.spi.ContentHandle;
import com.redhat.ceylon.cmr.spi.ContentOptions;
import com.redhat.ceylon.cmr.spi.Node;
import com.redhat.ceylon.cmr.spi.OpenNode;
import com.redhat.ceylon.common.Constants;
import com.redhat.ceylon.common.log.Logger;
import com.redhat.ceylon.model.cmr.RepositoryException;

/**
 * NPM content store.
 *
 * @author Tako Schotanus ([email protected])
 */
public class NpmContentStore extends AbstractContentStore {
    private final File out;
    private final FileContentStore[] stores;
    private final FileContentStore outstore;
    private String npmCommand;
    private String path;
    
    public NpmContentStore(File[] roots, File out, Logger log, boolean offline) {
        super(log, offline, -1);
        assert(roots.length > 0);
        this.stores = new FileContentStore[roots.length];
        int i = 0;
        for (File root : roots) {
            stores[i++] = new FileContentStore(root);
        }
        this.out = out;
        if (out != null) {
            outstore = new FileContentStore(out);
        } else {
            outstore = null;
        }
    }

    public Iterable getBaseDirectories() {
        ArrayList baseDirectories = new ArrayList<>(stores.length);
        for (FileContentStore store : stores) {
            for (File baseDir : store.getBaseDirectories()) {
                baseDirectories.add(baseDir);
            }
        }
        baseDirectories.add(out);
        return baseDirectories;
    }
    
    public OpenNode createRoot() {
        return new RootNode(this, this);
    }

    public OpenNode find(Node parent, String child) {
        DefaultNode node = null;
        if (hasContent(child) == false) {
            node = new DefaultNode(child);
            node.setContentMarker();
            return node;
        } else {
            if (ArtifactContext.getSuffixFromFilename(child).equals(ArtifactContext.JS)) {
                child = getTrueArtifactName(parent);
            }
            for (FileContentStore store : stores) {
                OpenNode result = store.find(parent, child);
                if (result != null) {
                    return result;
                }
            }
            installNpmModule(parent);
            return outstore.find(parent, child);
        }
    }

    public ContentHandle peekContent(Node node) {
        for (FileContentStore store : stores) {
            ContentHandle result = store.peekContent(node);
            if (result != null) {
                return result;
            }
        }
        return null;
    }

    private String getTrueArtifactName(Node parent) {
        Node node = parent.getChild("package.json");
        try {
            File json = node.getContent(File.class);
            if (json.exists() && json.isFile() && json.canRead()) {
                //Parse json, get "main", that's the file we need
                try (FileReader reader = new FileReader(json)){
                    Map descriptor = JavaScriptResolver.readNpmDescriptor(json);
                    Object main = descriptor.get("main");
                    if (main instanceof String) {
                        return (String)main;
                    } else if (main == null) {
                        return "index.js";
                    } else {
                        throw new RepositoryException("unexpected value for 'main' in npm descriptor: " + json);
                    }
                }
            } else {
                throw new RepositoryException("npm descriptor not found: " + json);
            }
        } catch (IOException ex) {
            throw new RepositoryException("error reading npm descriptor: " + out + "/package.json", ex);
        }
    }
    
    public ContentHandle getContent(Node node) throws IOException {
        ContentHandle result = peekContent(node);
        if (result != null) {
            return result;
        }
        
        if (outstore != null) {
            installNpmModule(node);
            return outstore.getContent(node);
        } else {
            // Until now we used peekContent() which doesn't throw
            // so we re-try with any store to get the error we want
            return stores[0].getContent(node);
        }
    }

    /**
     * 
     */
    public void installNpmModule(Node node) {
        try {
            if (!out.exists()) {
                out.mkdirs();
            }
            ArtifactContext ac = ArtifactContext.fromNode(node);
            String name = ac.getName();
            String version = ac.getVersion();
            String module = version.isEmpty() ? name : name + "@" + version;
            if (log != null) log.debug("installing npm module " + module + " in " + out);
            String npmCmd = npmCommand != null ? npmCommand : System.getProperty(Constants.PROP_CEYLON_EXTCMD_NPM, "npm");
            ProcessBuilder pb = new ProcessBuilder()
                    .command(npmCmd, "install", "--silent", "--no-bin-links", module)
                    .directory(out.getParentFile())
                    .inheritIO();
            Map env = pb.environment();
            String pathVariableName = "PATH";
            for (String key : env.keySet()) {
                if (key.equalsIgnoreCase("path")) {
                    pathVariableName = key;
                    break;
                }
            }
            String pathForRunningNpm = path != null ? path : System.getProperty(Constants.PROP_CEYLON_EXTCMD_PATH, System.getenv("PATH"));
            env.put(pathVariableName, pathForRunningNpm);
            
            Process p = pb.start();
            p.waitFor();
            if (p.exitValue() != 0) {
                throw new RepositoryException("npm installer failed with exit code: " + p.exitValue());
            }
        } catch (InterruptedException | IOException ex) {
            throw new RepositoryException("error running npm installer (make sure 'npm' is installed and available in your PATH)", ex);
        }
    }

    public ContentHandle putContent(Node node, InputStream stream, ContentOptions options) throws IOException {
        if (outstore != null) {
            return outstore.putContent(node, stream, options);
        } else {
            return null;  // no output specified
        }
    }

    public OpenNode create(Node parent, String child) {
        if (outstore != null) {
            return outstore.create(parent, child);
        } else {
            return null;  // no output specified
        }
    }

    public Iterable find(Node parent) {
        return Collections.emptyList(); // cannot find all children
    }
    
    public String getDisplayString() {
        String name = "npm";
        if (offline) {
            name += " (offline)";
        }
        return name;
    }

    @Override
    public boolean isHerd() {
        return false;
    }

    @Override
    public boolean canHandleFolders() {
        return false;
    }

    public void setNpmCommand(String npmCommand) {
        this.npmCommand = npmCommand;
    }

    public void setPathForRunningNpm(String path) {
        this.path = path;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy