dylanpyle / svgbundler

Track down any dependencies of an SVG image and embed them inline.
MIT License
1 stars 0 forks source link

Doesn't preserve element order #1

Open dylanpyle opened 6 years ago

dylanpyle commented 6 years ago

SVG elements are grouped by element type in the output, rather than preserving their original order.

There's a flag in xml2js to fix this, but then rebuilding the XML afterwards from the generated JSON returns garbage.

Possible solutions:

sirarthurnell commented 5 years ago

@dylanpyle I would vote for finding a more robust one. I have lost the whole day trying to make the MSBuild work with some modifications made with xml2js and it doesn't work because MSBuild requires nodes to be in order (another sh!t). Well, here's what I have tried:

const fs = require('fs');
const path = require('path');
const xml2js = require('xml2js');
const promisify = require('util').promisify;

const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);

/**
 * Main.
 */
(async function main() {
  const projectFileName = 'source-project.csproj';
  const newProjectFileName = 'new-project.csproj';
  const currentDirectory = path.resolve('.');
  const projectFilePath = path.join(currentDirectory, projectFileName);
  const newProjectFilePath = path.join(currentDirectory, newProjectFileName);

  const structure = await getProjectStructure(projectFilePath);
  saveProjectStructure(structure, newProjectFilePath);
})();

/**
 * Load the project structure from the project file.
 * @param {string} projectFilePath Path to the project file.
 */
async function getProjectStructure(projectFilePath) {
  const contents = await readFile(projectFilePath, 'utf8');
  const parserPromise = new Promise((resolve, reject) => {
    const parser = new xml2js.Parser({
      preserveChildrenOrder: true,
      explicitArray: false
    });
    parser.parseString(contents, (err, structure) => {
      if (err) {
        reject(err);
      } else {
        resolve(structure);
      }
    });
  });

  return parserPromise;
}

/**
 * Saves the modified project file to disk.
 * @param {object} structure Modified structure.
 * @param {string} newProjectFilePath Path to the new project file.
 */
async function saveProjectStructure(structure, newProjectFilePath) {
  const builder = new xml2js.Builder({
    xmldec: {
      version: '1.0',
      encoding: 'UTF-8'
    }
  });

  const xml = builder.buildObject(structure);

  await writeFile(newProjectFilePath, xml, 'utf8');
}

The source file is (I reduced it in size because the original one is really big):

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.Default.props" Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.Default.props')" />
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>
    </ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{244B8B65-A692-4CB3-A2E7-126B8817A479}</ProjectGuid>
    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Planificacion</RootNamespace>
    <AssemblyName>Planificacion</AssemblyName>
    <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
    <MvcBuildViews>false</MvcBuildViews>
    <UseIISExpress>false</UseIISExpress>
    <IISExpressSSLPort />
    <IISExpressAnonymousAuthentication />
    <IISExpressWindowsAuthentication />
    <IISExpressUseClassicPipelineMode />
    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
    <RestorePackages>true</RestorePackages>
    <SccProjectName>Svn</SccProjectName>
    <SccLocalPath>Svn</SccLocalPath>
    <SccAuxPath>Svn</SccAuxPath>
    <SccProvider>SubversionScc</SccProvider>
    <Use64BitIISExpress />
    <UseGlobalApplicationHostFile />
    <TypeScriptToolsVersion>3.1</TypeScriptToolsVersion>
  </PropertyGroup>  
  <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>Este proyecto hace referencia a los paquetes NuGet que faltan en este equipo. Habilite la restauración del paquete NuGet para descargarlos. Para obtener más información, consulte http://go.microsoft.com/fwlink/?LinkID=322105. El archivo que falta es {0}.</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
  </Target>
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target> -->
</Project>

And the outcome:

<?xml version="1.0" encoding="UTF-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.Default.props" Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.Default.props')"/>
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')"/>
  <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')"/>
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>&#xD;
    </ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{244B8B65-A692-4CB3-A2E7-126B8817A479}</ProjectGuid>
    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Planificacion</RootNamespace>
    <AssemblyName>Planificacion</AssemblyName>
    <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
    <MvcBuildViews>false</MvcBuildViews>
    <UseIISExpress>false</UseIISExpress>
    <IISExpressSSLPort/>
    <IISExpressAnonymousAuthentication/>
    <IISExpressWindowsAuthentication/>
    <IISExpressUseClassicPipelineMode/>
    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
    <RestorePackages>true</RestorePackages>
    <SccProjectName>Svn</SccProjectName>
    <SccLocalPath>Svn</SccLocalPath>
    <SccAuxPath>Svn</SccAuxPath>
    <SccProvider>SubversionScc</SccProvider>
    <Use64BitIISExpress/>
    <UseGlobalApplicationHostFile/>
    <TypeScriptToolsVersion>3.1</TypeScriptToolsVersion>
  </PropertyGroup>
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>Este proyecto hace referencia a los paquetes NuGet que faltan en este equipo. Habilite la restauración del paquete NuGet para descargarlos. Para obtener más información, consulte http://go.microsoft.com/fwlink/?LinkID=322105. El archivo que falta es {0}.</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))"/>
  </Target>
</Project>

Notice the <Import> elements in the first file and then, in the second. They are grouped at the beginning instead of preserving the order. But accordly to my tests with the larger files, sometimes they get grouped by type at the end of the parent node.

Do not close this issue until is solved, please. I suggest using real fat XML files to test it, not toy ones.

dylanpyle commented 5 years ago

@sirarthurnell Thanks for the detailed report :)

I don't plan to close this issue, but unfortunately it's pretty unlikely that I'll have time to look into fixing it in the near future. We're no longer using this library in production, as we moved towards a totally difference image processing pipeline.

Very happily accepting pull requests if anyone finds this useful and wants to try to improve it - I think the two original solutions are still good starting points.