packages feed

sourcemap-0.1.6.1: test/Node.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

-- | This test suite requires the source-map node package. That is the
-- reference implementation, so we generate sourcemaps and compare
-- them with what the source-map package would generate.

module Main where

import           SourceMap
import           SourceMap.Types

import           Data.Aeson
import qualified Data.ByteString.Lazy.Char8 as Bytes
import qualified Data.ByteString.Lazy.UTF8 as Bytes
import           Data.List
import           System.Exit
import           System.Process

-- | Run the test suite.
main :: IO ()
main =
  compareImplementations
    SourceMapping { smFile = "out.js"
                  , smSourceRoot = Nothing
                  , smMappings = mappings  }

-- | Compare the Haskell implementation with the JavaScript
-- implementation for the given source mapping.
compareImplementations :: SourceMapping -> IO ()
compareImplementations m = do
  !n <- generateNode m
  let !h = generate m
  if h == n
     then exitSuccess
     else do putStr "Node:    "; Bytes.putStrLn $ encode n
             putStr "Haskell: "; Bytes.putStrLn $ encode h
             exitFailure

-- | Generate a source map using the nodejs source-map package.
generateNode :: SourceMapping -> IO Value
generateNode SourceMapping{..} = do
  (ret, out, err) <- readProcessWithExitCode "node" [] source
  if ret /= ExitSuccess
    then error err
    else
      case decode (Bytes.fromString (concat (lines out))) of
         Just value -> return value
         Nothing    -> error "unable to parse node's json output"

  where source = unlines $
          ["var s = require('source-map');"
          ,""
          ,"var generator = new s.SourceMapGenerator({"] ++
          [intercalate "," (["file: " ++ show smFile] ++
                           ["sourceRoot: " ++ show root | Just root <- [smSourceRoot]])] ++
          ["});"
          ,""] ++
          map addMapping smMappings ++
          ["console.log(generator.toString())"]
        addMapping Mapping{..} = unlines $
            ["generator.addMapping({"
            ,"  generated: " ++ showPos mapGenerated ++ ","] ++
            ["  original: " ++ showPos orig ++ "," | Just orig <- [mapOriginal]] ++
            ["  source: " ++ show src ++ "," | Just src <- [mapSourceFile]] ++
            ["  name: " ++ show name | Just name <- [mapName]] ++
            ["});"]
        showPos Pos{..} = "{ line: " ++ show posLine ++ ", column: " ++ show posColumn ++ " }"

-- | A sample source mapping.
mappings :: [Mapping]
mappings =
  [Mapping {
        mapGenerated = Pos 1 1,
        mapOriginal = Just (Pos 1 1),
        mapSourceFile = Just "one.js",
        mapName = Nothing
  }
  ,Mapping {
        mapGenerated = Pos 1 5,
        mapOriginal = Just (Pos 1 5),
        mapSourceFile = Just "one.js",
        mapName = Nothing
  }
  ,Mapping {
        mapGenerated = Pos 1 9,
        mapOriginal = Just (Pos 1 11),
        mapSourceFile = Just "one.js",
        mapName = Nothing
  }
  ,Mapping {
        mapGenerated = Pos 1 18,
        mapOriginal = Just (Pos 1 21),
        mapSourceFile = Just "one.js",
        mapName = Just "bar"
  }
  ,Mapping {
        mapGenerated = Pos 1 21,
        mapOriginal = Just (Pos 2 3),
        mapSourceFile = Just "one.js",
        mapName = Nothing
  }
  ,Mapping {
        mapGenerated = Pos 1 28,
        mapOriginal = Just (Pos 2 10),
        mapSourceFile = Just "one.js",
        mapName = Just "baz"
  }
  ,Mapping {
        mapGenerated = Pos 1 32,
        mapOriginal = Just (Pos 2 14),
        mapSourceFile = Just "one.js",
        mapName = Just "bar"
  }
  ,Mapping {
        mapGenerated = Pos 2 1,
        mapOriginal = Just (Pos 1 1),
        mapSourceFile = Just "two.js",
        mapName = Nothing
  }
  ,Mapping {
        mapGenerated = Pos 2 5,
        mapOriginal = Just (Pos 1 5),
        mapSourceFile = Just "two.js",
        mapName = Nothing
  }
  ,Mapping {
        mapGenerated = Pos 2 9,
        mapOriginal = Just (Pos 1 11),
        mapSourceFile = Just "two.js",
        mapName = Nothing
  },
  Mapping {
         mapGenerated = Pos 2 18,
         mapOriginal = Just (Pos 1 21),
         mapSourceFile = Just "two.js",
         mapName = Just "n"
   },
   Mapping {
         mapGenerated = Pos 2 21,
         mapOriginal = Just (Pos 2 3),
         mapSourceFile = Just "two.js",
         mapName = Nothing
   },
   Mapping {
         mapGenerated = Pos 2 28,
         mapOriginal = Just (Pos 2 10),
         mapSourceFile = Just "two.js",
         mapName = Just "n"
   }]